Compare commits
3 Commits
reimplemen
...
fix-select
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
df58f1e87d | ||
|
|
87a4a7272d | ||
|
|
4b03efcef4 |
@@ -54,9 +54,6 @@
|
||||
openmct.install(openmct.plugins.AutoflowView({
|
||||
type: "telemetry.panel"
|
||||
}));
|
||||
openmct.install(openmct.plugins.DisplayLayout({
|
||||
showAsView: ['summary-widget', 'example.imagery']
|
||||
}));
|
||||
openmct.install(openmct.plugins.Conductor({
|
||||
menuOptions: [
|
||||
{
|
||||
@@ -86,7 +83,6 @@
|
||||
openmct.install(openmct.plugins.LADTable());
|
||||
openmct.install(openmct.plugins.Filters(['table', 'telemetry.plot.overlay']));
|
||||
openmct.install(openmct.plugins.ObjectMigration());
|
||||
openmct.install(openmct.plugins.GoToOriginalAction());
|
||||
openmct.start();
|
||||
</script>
|
||||
</html>
|
||||
|
||||
@@ -31,6 +31,7 @@ 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",
|
||||
@@ -51,6 +52,7 @@ define([
|
||||
NavigateAction,
|
||||
OrphanNavigationHandler,
|
||||
NewTabAction,
|
||||
WindowTitler,
|
||||
browseTemplate,
|
||||
browseObjectTemplate,
|
||||
objectHeaderTemplate,
|
||||
@@ -224,6 +226,14 @@ define([
|
||||
}
|
||||
],
|
||||
"runs": [
|
||||
{
|
||||
"implementation": WindowTitler,
|
||||
"depends": [
|
||||
"navigationService",
|
||||
"$rootScope",
|
||||
"$document"
|
||||
]
|
||||
},
|
||||
{
|
||||
"implementation": OrphanNavigationHandler,
|
||||
"depends": [
|
||||
|
||||
@@ -19,10 +19,33 @@
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
import GoToOriginalAction from './goToOriginalAction';
|
||||
|
||||
export default function () {
|
||||
return function (openmct) {
|
||||
openmct.contextMenu.registerAction(new GoToOriginalAction(openmct));
|
||||
};
|
||||
}
|
||||
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;
|
||||
}
|
||||
);
|
||||
78
platform/commonUI/browse/test/windowing/WindowTitlerSpec.js
Normal file
78
platform/commonUI/browse/test/windowing/WindowTitlerSpec.js
Normal file
@@ -0,0 +1,78 @@
|
||||
/*****************************************************************************
|
||||
* 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");
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -65,8 +65,7 @@ define([
|
||||
"depends": [
|
||||
"$document",
|
||||
"$compile",
|
||||
"$rootScope",
|
||||
"$timeout"
|
||||
"$rootScope"
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<div class="c-message"
|
||||
<div class="l-message"
|
||||
ng-class="'message-severity-' + ngModel.severity">
|
||||
<div class="w-message-contents">
|
||||
<div class="c-message__top-bar">
|
||||
<div class="c-message__title">{{ngModel.title}}</div>
|
||||
<div class="top-bar">
|
||||
<div class="title">{{ngModel.title}}</div>
|
||||
</div>
|
||||
<div class="c-message__hint" ng-hide="ngModel.hint === undefined">
|
||||
<div class="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="c-overlay__button-bar">
|
||||
<button ng-repeat="dialogOption in ngModel.options"
|
||||
class="c-button"
|
||||
<div class="bottom-bar">
|
||||
<a ng-repeat="dialogOption in ngModel.options"
|
||||
class="s-button"
|
||||
ng-click="dialogOption.callback()">
|
||||
{{dialogOption.label}}
|
||||
</button>
|
||||
<button class="c-button c-button--major"
|
||||
</a>
|
||||
<a class="s-button major"
|
||||
ng-if="ngModel.primaryOption"
|
||||
ng-click="ngModel.primaryOption.callback()">
|
||||
{{ngModel.primaryOption.label}}
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,25 +1,25 @@
|
||||
<div class="c-message"
|
||||
<div class="l-message"
|
||||
ng-class="'message-severity-' + ngModel.severity">
|
||||
<div class="w-message-contents">
|
||||
<div class="c-message__top-bar">
|
||||
<div class="c-message__title">{{ngModel.message}}</div>
|
||||
<div class="top-bar">
|
||||
<div class="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>
|
||||
<div class="c-overlay__button-bar">
|
||||
<button ng-repeat="dialogOption in ngModel.options"
|
||||
class="c-button"
|
||||
<div class="bottom-bar">
|
||||
<a ng-repeat="dialogOption in ngModel.options"
|
||||
class="s-button"
|
||||
ng-click="dialogOption.callback()">
|
||||
{{dialogOption.label}}
|
||||
</button>
|
||||
<button class="c-button c-button--major"
|
||||
{{dialogOption.label}}
|
||||
</a>
|
||||
<a class="s-button major"
|
||||
ng-if="ngModel.primaryOption"
|
||||
ng-click="ngModel.primaryOption.callback()">
|
||||
{{ngModel.primaryOption.label}}
|
||||
</button>
|
||||
{{ngModel.primaryOption.label}}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
<mct-container key="overlay">
|
||||
<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 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>
|
||||
</div>
|
||||
<div class="w-messages c-overlay__messages">
|
||||
<div class="w-messages">
|
||||
<mct-include
|
||||
ng-repeat="msg in ngModel.dialog.messages | orderBy: '-'"
|
||||
key="'notification-message'" ng-model="msg.model"></mct-include>
|
||||
</div>
|
||||
<div class="c-overlay__bottom-bar">
|
||||
<button ng-repeat="dialogAction in ngModel.dialog.actions"
|
||||
class="c-button c-button--major"
|
||||
<div class="bottom-bar">
|
||||
<a ng-repeat="dialogAction in ngModel.dialog.actions"
|
||||
class="s-button major"
|
||||
ng-click="dialogAction.action()">
|
||||
{{dialogAction.label}}
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</mct-container>
|
||||
|
||||
@@ -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">
|
||||
<button ng-click="ngModel.cancel()"
|
||||
<a ng-click="ngModel.cancel()"
|
||||
ng-if="ngModel.cancel"
|
||||
class="c-click-icon c-overlay__close-button icon-x-in-circle"></button>
|
||||
class="c-click-icon c-overlay__close-button icon-x-in-circle"></a>
|
||||
<div class="c-overlay__contents" ng-transclude></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -44,9 +44,8 @@ define(
|
||||
* @memberof platform/commonUI/dialog
|
||||
* @constructor
|
||||
*/
|
||||
function OverlayService($document, $compile, $rootScope, $timeout) {
|
||||
function OverlayService($document, $compile, $rootScope) {
|
||||
this.$compile = $compile;
|
||||
this.$timeout = $timeout;
|
||||
|
||||
// Don't include $document and $rootScope directly;
|
||||
// avoids https://docs.angularjs.org/error/ng/cpws
|
||||
@@ -94,14 +93,12 @@ define(
|
||||
scope.key = key;
|
||||
scope.typeClass = typeClass || 't-dialog';
|
||||
|
||||
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);
|
||||
});
|
||||
// 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
|
||||
|
||||
@@ -35,20 +35,16 @@ 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", ["append"]);
|
||||
mockBody = jasmine.createSpyObj("body", ["prepend"]);
|
||||
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);
|
||||
@@ -58,8 +54,7 @@ define(
|
||||
overlayService = new OverlayService(
|
||||
mockDocument,
|
||||
mockCompile,
|
||||
mockRootScope,
|
||||
mockTimeout
|
||||
mockRootScope
|
||||
);
|
||||
});
|
||||
|
||||
@@ -72,7 +67,7 @@ define(
|
||||
|
||||
it("adds the templated element to the body", function () {
|
||||
overlayService.createOverlay("test", {});
|
||||
expect(mockBody.append).toHaveBeenCalledWith(mockElement);
|
||||
expect(mockBody.prepend).toHaveBeenCalledWith(mockElement);
|
||||
});
|
||||
|
||||
it("places the provided model/key in its template's scope", function () {
|
||||
|
||||
@@ -49,7 +49,7 @@ define(
|
||||
name: "Properties",
|
||||
rows: this.properties.map(function (property, index) {
|
||||
// Property definition is same as form row definition
|
||||
var row = JSON.parse(JSON.stringify(property.getDefinition()));
|
||||
var row = Object.create(property.getDefinition());
|
||||
row.key = index;
|
||||
return row;
|
||||
}).filter(function (row) {
|
||||
|
||||
@@ -162,6 +162,9 @@ function (
|
||||
function saveAfterClone(clonedObject) {
|
||||
return this.openmct.editor.save().then(() => {
|
||||
// Force mutation for search indexing
|
||||
clonedObject.useCapability('mutation', (model) => {
|
||||
return model;
|
||||
});
|
||||
return clonedObject;
|
||||
})
|
||||
}
|
||||
@@ -170,14 +173,6 @@ 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;
|
||||
@@ -199,7 +194,6 @@ function (
|
||||
.then(undirtyOriginals)
|
||||
.then(saveAfterClone)
|
||||
.then(finishEditing)
|
||||
.then(indexForSearch)
|
||||
.then(hideBlockingDialog)
|
||||
.then(onSuccess)
|
||||
.catch(onFailure);
|
||||
|
||||
@@ -64,6 +64,7 @@ define(
|
||||
* @returns boolean
|
||||
*/
|
||||
EditorCapability.prototype.inEditContext = function () {
|
||||
console.warn('DEPRECATION WARNING: isEditing checks must be done via openmct.editor.');
|
||||
return this.openmct.editor.isEditing();
|
||||
};
|
||||
|
||||
@@ -73,6 +74,7 @@ define(
|
||||
* @returns {*}
|
||||
*/
|
||||
EditorCapability.prototype.isEditContextRoot = function () {
|
||||
console.warn('DEPRECATION WARNING: isEditing checks must be done via openmct.editor.');
|
||||
return this.openmct.editor.isEditing();
|
||||
};
|
||||
|
||||
|
||||
@@ -71,12 +71,6 @@ 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
|
||||
@@ -88,9 +82,7 @@ define(
|
||||
|
||||
window.location.href = url;
|
||||
|
||||
if (isFirstViewEditable(object.useCapability('adapter'))) {
|
||||
openmct.editor.edit();
|
||||
}
|
||||
openmct.editor.edit();
|
||||
}
|
||||
|
||||
newModel.type = this.type.getKey();
|
||||
|
||||
@@ -66,7 +66,7 @@ define(
|
||||
name: "Properties",
|
||||
rows: this.properties.map(function (property, index) {
|
||||
// Property definition is same as form row definition
|
||||
var row = JSON.parse(JSON.stringify(property.getDefinition()));
|
||||
var row = Object.create(property.getDefinition());
|
||||
|
||||
// Use index as the key into the formValue;
|
||||
// this correlates to the indexing provided by
|
||||
|
||||
@@ -77,19 +77,14 @@ define([], function () {
|
||||
return promiseFn().then(nextFn);
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Clear any existing persistence calls for object with given ID. This ensures only the most recent persistence
|
||||
* call is executed. This should prevent stale objects being persisted and overwriting fresh ones.
|
||||
*/
|
||||
if (this.isScheduled(id)) {
|
||||
this.clearTransactionsFor(id);
|
||||
}
|
||||
|
||||
this.clearTransactionFns[id] =
|
||||
this.transactionService.addToTransaction(
|
||||
chain(onCommit, release),
|
||||
chain(onCancel, release)
|
||||
);
|
||||
if (!this.isScheduled(id)) {
|
||||
this.clearTransactionFns[id] =
|
||||
this.transactionService.addToTransaction(
|
||||
chain(onCommit, release),
|
||||
chain(onCancel, release)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -93,33 +93,24 @@ define(
|
||||
expect(mockOnCancel).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe("Adds callbacks to transaction", function () {
|
||||
beforeEach(function () {
|
||||
spyOn(manager, 'clearTransactionsFor');
|
||||
manager.clearTransactionsFor.and.callThrough();
|
||||
});
|
||||
it("ignores subsequent calls for the same object", function () {
|
||||
manager.addToTransaction(
|
||||
testId,
|
||||
jasmine.createSpy(),
|
||||
jasmine.createSpy()
|
||||
);
|
||||
expect(mockTransactionService.addToTransaction.calls.count())
|
||||
.toEqual(1);
|
||||
});
|
||||
|
||||
it("and clears pending calls if same object", function () {
|
||||
manager.addToTransaction(
|
||||
testId,
|
||||
jasmine.createSpy(),
|
||||
jasmine.createSpy()
|
||||
);
|
||||
expect(manager.clearTransactionsFor).toHaveBeenCalledWith(testId);
|
||||
});
|
||||
|
||||
it("and does not clear pending calls if different object", function () {
|
||||
manager.addToTransaction(
|
||||
'other-id',
|
||||
jasmine.createSpy(),
|
||||
jasmine.createSpy()
|
||||
);
|
||||
expect(manager.clearTransactionsFor).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
expect(mockTransactionService.addToTransaction.calls.count()).toEqual(2);
|
||||
});
|
||||
it("accepts subsequent calls for other objects", function () {
|
||||
manager.addToTransaction(
|
||||
'other-id',
|
||||
jasmine.createSpy(),
|
||||
jasmine.createSpy()
|
||||
);
|
||||
expect(mockTransactionService.addToTransaction.calls.count())
|
||||
.toEqual(2);
|
||||
});
|
||||
|
||||
it("does not remove callbacks from the transaction", function () {
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<div ng-controller="BannerController" ng-show="active.notification"
|
||||
class="c-message-banner {{active.notification.model.severity}}" ng-class="{
|
||||
class="l-message-banner s-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="c-message-banner__message">
|
||||
<span class="banner-elem label">
|
||||
{{active.notification.model.title}}
|
||||
</span>
|
||||
<span ng-show="active.notification.model.progress !== undefined || active.notification.model.unknownProgress">
|
||||
<mct-include key="'progress-bar'" class="c-message-banner__progress-bar"
|
||||
<mct-include key="'progress-bar'" class="banner-elem"
|
||||
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>
|
||||
<button class="c-message-banner__close-button c-click-icon icon-x-in-circle" ng-click="dismiss(active.notification, $event)"></button>
|
||||
<a class="banner-elem close icon-x" ng-click="dismiss(active.notification, $event)"></a>
|
||||
</div>
|
||||
|
||||
@@ -54,7 +54,6 @@ define(
|
||||
if (isDestroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
var removeSelectable = openmct.selection.selectable(
|
||||
element[0],
|
||||
scope.$eval(attrs.mctSelectable),
|
||||
|
||||
@@ -43,10 +43,23 @@ define([], function () {
|
||||
var mutationTopic = topic('mutation');
|
||||
mutationTopic.listen(function (domainObject) {
|
||||
var persistence = domainObject.getCapability('persistence');
|
||||
var wasActive = transactionService.isActive();
|
||||
cacheService.put(domainObject.getId(), domainObject.getModel());
|
||||
|
||||
if (hasChanged(domainObject)) {
|
||||
persistence.persist();
|
||||
|
||||
if (!wasActive) {
|
||||
transactionService.startTransaction();
|
||||
}
|
||||
|
||||
transactionService.addToTransaction(
|
||||
persistence.persist.bind(persistence),
|
||||
persistence.refresh.bind(persistence)
|
||||
);
|
||||
|
||||
if (!wasActive) {
|
||||
transactionService.commit();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -24,27 +24,22 @@ define(
|
||||
["../../src/runs/TransactingMutationListener"],
|
||||
function (TransactingMutationListener) {
|
||||
|
||||
describe("TransactingMutationListener", function () {
|
||||
xdescribe("TransactingMutationListener", function () {
|
||||
var mockTopic,
|
||||
mockMutationTopic,
|
||||
mockCacheService,
|
||||
mockTransactionService,
|
||||
mockDomainObject,
|
||||
mockModel,
|
||||
mockPersistence;
|
||||
|
||||
beforeEach(function () {
|
||||
mockTopic = jasmine.createSpy('topic');
|
||||
mockMutationTopic =
|
||||
jasmine.createSpyObj('mutation', ['listen']);
|
||||
mockCacheService =
|
||||
jasmine.createSpyObj('cacheService', [
|
||||
'put'
|
||||
]);
|
||||
mockTransactionService =
|
||||
jasmine.createSpyObj('transactionService', [
|
||||
'isActive',
|
||||
'startTransaction',
|
||||
'addToTransaction',
|
||||
'commit'
|
||||
]);
|
||||
mockDomainObject = jasmine.createSpyObj(
|
||||
@@ -57,24 +52,18 @@ define(
|
||||
);
|
||||
|
||||
mockTopic.and.callFake(function (t) {
|
||||
expect(t).toBe('mutation');
|
||||
return mockMutationTopic;
|
||||
return (t === 'mutation') && mockMutationTopic;
|
||||
});
|
||||
|
||||
mockDomainObject.getId.and.returnValue('mockId');
|
||||
mockDomainObject.getCapability.and.callFake(function (c) {
|
||||
expect(c).toBe('persistence');
|
||||
return mockPersistence;
|
||||
return (c === 'persistence') && mockPersistence;
|
||||
});
|
||||
mockModel = {};
|
||||
mockDomainObject.getModel.and.returnValue(mockModel);
|
||||
|
||||
mockPersistence.persisted.and.returnValue(true);
|
||||
|
||||
return new TransactingMutationListener(
|
||||
mockTopic,
|
||||
mockTransactionService,
|
||||
mockCacheService
|
||||
mockTransactionService
|
||||
);
|
||||
});
|
||||
|
||||
@@ -83,27 +72,48 @@ define(
|
||||
.toHaveBeenCalledWith(jasmine.any(Function));
|
||||
});
|
||||
|
||||
it("calls persist if the model has changed", function () {
|
||||
mockModel.persisted = Date.now();
|
||||
[false, true].forEach(function (isActive) {
|
||||
var verb = isActive ? "is" : "isn't";
|
||||
|
||||
//Mark the model dirty by setting the mutated date later than the last persisted date.
|
||||
mockModel.modified = mockModel.persisted + 1;
|
||||
function onlyWhenInactive(expectation) {
|
||||
return isActive ? expectation.not : expectation;
|
||||
}
|
||||
|
||||
mockMutationTopic.listen.calls.mostRecent()
|
||||
.args[0](mockDomainObject);
|
||||
describe("when a transaction " + verb + " active", function () {
|
||||
var innerVerb = isActive ? "does" : "doesn't";
|
||||
|
||||
expect(mockPersistence.persist).toHaveBeenCalled();
|
||||
});
|
||||
beforeEach(function () {
|
||||
mockTransactionService.isActive.and.returnValue(isActive);
|
||||
});
|
||||
|
||||
it("does not call persist if the model has not changed", function () {
|
||||
mockModel.persisted = Date.now();
|
||||
describe("and mutation occurs", function () {
|
||||
beforeEach(function () {
|
||||
mockMutationTopic.listen.calls.mostRecent()
|
||||
.args[0](mockDomainObject);
|
||||
});
|
||||
|
||||
mockModel.modified = mockModel.persisted;
|
||||
|
||||
mockMutationTopic.listen.calls.mostRecent()
|
||||
.args[0](mockDomainObject);
|
||||
it(innerVerb + " start a new transaction", function () {
|
||||
onlyWhenInactive(
|
||||
expect(mockTransactionService.startTransaction)
|
||||
).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
expect(mockPersistence.persist).not.toHaveBeenCalled();
|
||||
it("adds to the active transaction", function () {
|
||||
expect(mockTransactionService.addToTransaction)
|
||||
.toHaveBeenCalledWith(
|
||||
jasmine.any(Function),
|
||||
jasmine.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
it(innerVerb + " immediately commit", function () {
|
||||
onlyWhenInactive(
|
||||
expect(mockTransactionService.commit)
|
||||
).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ define([
|
||||
"./src/actions/MoveAction",
|
||||
"./src/actions/CopyAction",
|
||||
"./src/actions/LinkAction",
|
||||
"./src/actions/GoToOriginalAction",
|
||||
"./src/actions/SetPrimaryLocationAction",
|
||||
"./src/services/LocatingCreationDecorator",
|
||||
"./src/services/LocatingObjectDecorator",
|
||||
@@ -40,6 +41,7 @@ define([
|
||||
MoveAction,
|
||||
CopyAction,
|
||||
LinkAction,
|
||||
GoToOriginalAction,
|
||||
SetPrimaryLocationAction,
|
||||
LocatingCreationDecorator,
|
||||
LocatingObjectDecorator,
|
||||
@@ -102,6 +104,14 @@ define([
|
||||
"linkService"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "follow",
|
||||
"name": "Go To Original",
|
||||
"description": "Go to the original, un-linked instance of this object.",
|
||||
"cssClass": "",
|
||||
"category": "contextual",
|
||||
"implementation": GoToOriginalAction
|
||||
},
|
||||
{
|
||||
"key": "locate",
|
||||
"name": "Set Primary Location",
|
||||
|
||||
60
platform/entanglement/src/actions/GoToOriginalAction.js
Normal file
60
platform/entanglement/src/actions/GoToOriginalAction.js
Normal file
@@ -0,0 +1,60 @@
|
||||
/*****************************************************************************
|
||||
* 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 () {
|
||||
|
||||
/**
|
||||
* Implements the "Go To Original" action, which follows a link back
|
||||
* to an original instance of an object.
|
||||
*
|
||||
* @implements {Action}
|
||||
* @constructor
|
||||
* @private
|
||||
* @memberof platform/entanglement
|
||||
* @param {ActionContext} context the context in which the action
|
||||
* will be performed
|
||||
*/
|
||||
function GoToOriginalAction(context) {
|
||||
this.domainObject = context.domainObject;
|
||||
}
|
||||
|
||||
GoToOriginalAction.prototype.perform = function () {
|
||||
return this.domainObject.getCapability("location").getOriginal()
|
||||
.then(function (originalObject) {
|
||||
var actionCapability =
|
||||
originalObject.getCapability("action");
|
||||
return actionCapability &&
|
||||
actionCapability.perform("navigate");
|
||||
});
|
||||
};
|
||||
|
||||
GoToOriginalAction.appliesTo = function (context) {
|
||||
var domainObject = context.domainObject;
|
||||
return domainObject && domainObject.hasCapability("location") &&
|
||||
domainObject.getCapability("location").isLink();
|
||||
};
|
||||
|
||||
return GoToOriginalAction;
|
||||
}
|
||||
);
|
||||
|
||||
93
platform/entanglement/test/actions/GoToOriginalActionSpec.js
Normal file
93
platform/entanglement/test/actions/GoToOriginalActionSpec.js
Normal file
@@ -0,0 +1,93 @@
|
||||
/*****************************************************************************
|
||||
* 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(
|
||||
[
|
||||
'../../src/actions/GoToOriginalAction',
|
||||
'../DomainObjectFactory',
|
||||
'../ControlledPromise'
|
||||
],
|
||||
function (GoToOriginalAction, domainObjectFactory, ControlledPromise) {
|
||||
|
||||
describe("The 'go to original' action", function () {
|
||||
var testContext,
|
||||
originalDomainObject,
|
||||
mockLocationCapability,
|
||||
mockOriginalActionCapability,
|
||||
originalPromise,
|
||||
action;
|
||||
|
||||
beforeEach(function () {
|
||||
mockLocationCapability = jasmine.createSpyObj(
|
||||
'location',
|
||||
['isLink', 'isOriginal', 'getOriginal']
|
||||
);
|
||||
mockOriginalActionCapability = jasmine.createSpyObj(
|
||||
'action',
|
||||
['perform', 'getActions']
|
||||
);
|
||||
originalPromise = new ControlledPromise();
|
||||
mockLocationCapability.getOriginal.and.returnValue(originalPromise);
|
||||
mockLocationCapability.isLink.and.returnValue(true);
|
||||
mockLocationCapability.isOriginal.and.callFake(function () {
|
||||
return !mockLocationCapability.isLink();
|
||||
});
|
||||
testContext = {
|
||||
domainObject: domainObjectFactory({
|
||||
capabilities: {
|
||||
location: mockLocationCapability
|
||||
}
|
||||
})
|
||||
};
|
||||
originalDomainObject = domainObjectFactory({
|
||||
capabilities: {
|
||||
action: mockOriginalActionCapability
|
||||
}
|
||||
});
|
||||
|
||||
action = new GoToOriginalAction(testContext);
|
||||
});
|
||||
|
||||
it("is applicable to links", function () {
|
||||
expect(GoToOriginalAction.appliesTo(testContext))
|
||||
.toBeTruthy();
|
||||
});
|
||||
|
||||
it("is not applicable to originals", function () {
|
||||
mockLocationCapability.isLink.and.returnValue(false);
|
||||
expect(GoToOriginalAction.appliesTo(testContext))
|
||||
.toBeFalsy();
|
||||
});
|
||||
|
||||
it("navigates to original objects when performed", function () {
|
||||
expect(mockOriginalActionCapability.perform)
|
||||
.not.toHaveBeenCalled();
|
||||
action.perform();
|
||||
originalPromise.resolve(originalDomainObject);
|
||||
expect(mockOriginalActionCapability.perform)
|
||||
.toHaveBeenCalledWith('navigate');
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -24,10 +24,10 @@
|
||||
<button ng-click="timer.clickStopButton()"
|
||||
ng-hide="timer.timerState == 'stopped'"
|
||||
title="Reset"
|
||||
class="c-timer__ctrl-reset c-icon-button c-icon-button--major icon-reset"></button>
|
||||
class="c-timer__ctrl-reset c-click-icon c-click-icon--major icon-reset"></button>
|
||||
<button ng-click="timer.clickButton()"
|
||||
title="{{timer.buttonText()}}"
|
||||
class="c-timer__ctrl-pause-play c-icon-button c-icon-button--major {{timer.buttonCssClass()}}"></button>
|
||||
class="c-timer__ctrl-pause-play c-click-icon c-click-icon--major {{timer.buttonCssClass()}}"></button>
|
||||
</div>
|
||||
<div class="c-timer__direction {{timer.signClass()}}"
|
||||
ng-hide="!timer.signClass()"></div>
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
<div class="t-imagery c-imagery" ng-controller="ImageryController as imagery">
|
||||
<div class="t-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">
|
||||
<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">
|
||||
<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">
|
||||
<input class="icon-brightness" type="range"
|
||||
min="0"
|
||||
max="500"
|
||||
ng-model="filters.brightness" />
|
||||
ng-model="filters.brightness">
|
||||
</input>
|
||||
<input class="icon-contrast" type="range"
|
||||
min="0"
|
||||
max="500"
|
||||
ng-model="filters.contrast" />
|
||||
ng-model="filters.contrast">
|
||||
</input>
|
||||
</span>
|
||||
<span class="holder flex-elem t-reset-btn-holder c-imagery__lc__reset-btn">
|
||||
<span class="holder flex-elem t-reset-btn-holder">
|
||||
<a class="s-icon-button icon-reset t-btn-reset"
|
||||
ng-click="filters = { brightness: 100, contrast: 100 }"></a>
|
||||
</span>
|
||||
|
||||
@@ -37,17 +37,75 @@ define(
|
||||
}
|
||||
|
||||
/**
|
||||
* Discard failures
|
||||
* Handle persistence failures by providing the user with a
|
||||
* dialog summarizing these failures, and giving the option
|
||||
* to overwrite/cancel as appropriate.
|
||||
* @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 =
|
||||
@@ -60,7 +118,19 @@ define(
|
||||
return $q.all(failuresToDiscard.map(discard));
|
||||
}
|
||||
|
||||
return discardAll(revisionErrors);
|
||||
// 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 PersistenceFailureHandler;
|
||||
|
||||
@@ -74,14 +74,43 @@ define(
|
||||
handler = new PersistenceFailureHandler(mockQ, mockDialogService);
|
||||
});
|
||||
|
||||
it("discards on handle", function () {
|
||||
it("shows a dialog to handle failures", 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();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
10
src/MCT.js
10
src/MCT.js
@@ -248,6 +248,7 @@ 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());
|
||||
@@ -256,6 +257,7 @@ define([
|
||||
if (typeof BUILD_CONSTANTS !== 'undefined') {
|
||||
this.install(buildInfoPlugin(BUILD_CONSTANTS));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
MCT.prototype = Object.create(EventEmitter.prototype);
|
||||
@@ -326,12 +328,6 @@ 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;
|
||||
}
|
||||
@@ -356,7 +352,7 @@ define([
|
||||
legacyRegistry.enable('adapter');
|
||||
|
||||
this.router.route(/^\/$/, () => {
|
||||
this.router.setPath('/browse/');
|
||||
this.router.setPath('/browse/mine');
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
@@ -33,25 +33,20 @@ export default class LegacyContextMenuAction {
|
||||
}
|
||||
|
||||
invoke(objectPath) {
|
||||
this.openmct.objects.getRoot().then((root) => {
|
||||
let pathWithRoot = objectPath.slice();
|
||||
pathWithRoot.push(root);
|
||||
let context = {
|
||||
category: 'contextual',
|
||||
domainObject: this.openmct.legacyObject(objectPath)
|
||||
}
|
||||
let legacyAction = new this.LegacyAction(context);
|
||||
|
||||
let context = {
|
||||
category: 'contextual',
|
||||
domainObject: this.openmct.legacyObject(pathWithRoot)
|
||||
}
|
||||
let legacyAction = new this.LegacyAction(context);
|
||||
|
||||
if (!legacyAction.getMetadata) {
|
||||
let metadata = Object.create(this.LegacyAction.definition);
|
||||
metadata.context = context;
|
||||
legacyAction.getMetadata = function () {
|
||||
return metadata;
|
||||
}.bind(legacyAction);
|
||||
}
|
||||
legacyAction.perform();
|
||||
});
|
||||
if (!legacyAction.getMetadata) {
|
||||
let metadata = Object.create(this.LegacyAction.definition);
|
||||
metadata.context = context;
|
||||
legacyAction.getMetadata = function () {
|
||||
return metadata;
|
||||
}.bind(legacyAction);
|
||||
}
|
||||
legacyAction.perform();
|
||||
}
|
||||
|
||||
appliesTo(objectPath) {
|
||||
|
||||
@@ -137,7 +137,8 @@ define([
|
||||
function callbackWrapper(series) {
|
||||
callback(createDatum(domainObject, metadata, series, series.getPointCount() - 1));
|
||||
}
|
||||
return capability.subscribe(callbackWrapper, request) || function () {};
|
||||
|
||||
return capability.subscribe(callbackWrapper, request);
|
||||
};
|
||||
|
||||
LegacyTelemetryProvider.prototype.supportsLimits = function (domainObject) {
|
||||
|
||||
@@ -57,10 +57,8 @@ define([
|
||||
}.bind(this);
|
||||
|
||||
handleLegacyMutation = function (legacyObject) {
|
||||
var newStyleObject = utils.toNewFormat(legacyObject.getModel(), legacyObject.getId()),
|
||||
keystring = utils.makeKeyString(newStyleObject.identifier);
|
||||
|
||||
this.eventEmitter.emit(keystring + ":*", newStyleObject);
|
||||
var newStyleObject = utils.toNewFormat(legacyObject.getModel(), legacyObject.getId());
|
||||
this.eventEmitter.emit(newStyleObject.identifier.key + ":*", newStyleObject);
|
||||
this.eventEmitter.emit('mutation', newStyleObject);
|
||||
}.bind(this);
|
||||
|
||||
|
||||
@@ -45,30 +45,15 @@ define([
|
||||
view: function (domainObject) {
|
||||
let $rootScope = openmct.$injector.get('$rootScope');
|
||||
let templateLinker = openmct.$injector.get('templateLinker');
|
||||
let scope = $rootScope.$new(true);
|
||||
let scope = $rootScope.$new();
|
||||
let legacyObject = convertToLegacyObject(domainObject);
|
||||
let isDestroyed = false;
|
||||
let unlistenToStatus;
|
||||
let element;
|
||||
scope.domainObject = legacyObject;
|
||||
scope.model = legacyObject.getModel();
|
||||
let child;
|
||||
let parent;
|
||||
|
||||
|
||||
return {
|
||||
show: function (container) {
|
||||
parent = container;
|
||||
child = document.createElement('div');
|
||||
parent.appendChild(child);
|
||||
let statusCapability = legacyObject.getCapability('status');
|
||||
unlistenToStatus = statusCapability.listen((newStatus) => {
|
||||
child.classList.remove('s-status-timeconductor-unsynced');
|
||||
|
||||
if (newStatus.includes('timeconductor-unsynced')) {
|
||||
child.classList.add('s-status-timeconductor-unsynced');
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: implement "gestures" support ?
|
||||
let uses = legacyView.uses || [];
|
||||
let promises = [];
|
||||
@@ -89,13 +74,12 @@ define([
|
||||
uses.forEach(function (key, i) {
|
||||
scope[key] = results[i];
|
||||
});
|
||||
element = openmct.$angular.element(child);
|
||||
templateLinker.link(
|
||||
scope,
|
||||
element,
|
||||
openmct.$angular.element(container),
|
||||
legacyView
|
||||
);
|
||||
child.classList.add('u-contents');
|
||||
container.classList.add('u-contents');
|
||||
}
|
||||
|
||||
if (promises.length) {
|
||||
@@ -109,12 +93,7 @@ define([
|
||||
}
|
||||
},
|
||||
destroy: function () {
|
||||
element.off();
|
||||
element.remove();
|
||||
scope.$destroy();
|
||||
element = null;
|
||||
scope = null;
|
||||
unlistenToStatus();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -25,34 +25,25 @@ define([
|
||||
cssClass: representation.cssClass,
|
||||
description: representation.description,
|
||||
canView: function (selection) {
|
||||
if (selection.length === 0 || selection[0].length === 0) {
|
||||
if (!selection[0] || !selection[0].context.item) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let selectionContext = selection[0][0].context;
|
||||
|
||||
if (!selectionContext.item) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return selectionContext.item.type === typeDefinition.key;
|
||||
let domainObject = selection[0].context.item;
|
||||
return domainObject.type === typeDefinition.key;
|
||||
},
|
||||
view: function (selection) {
|
||||
let domainObject = selection[0][0].context.item;
|
||||
let domainObject = selection[0].context.item;
|
||||
let $rootScope = openmct.$injector.get('$rootScope');
|
||||
let templateLinker = openmct.$injector.get('templateLinker');
|
||||
let scope = $rootScope.$new(true);
|
||||
let scope = $rootScope.$new();
|
||||
let legacyObject = convertToLegacyObject(domainObject);
|
||||
let isDestroyed = false;
|
||||
let element;
|
||||
scope.domainObject = legacyObject;
|
||||
scope.model = legacyObject.getModel();
|
||||
|
||||
|
||||
return {
|
||||
show: function (container) {
|
||||
let child = document.createElement('div');
|
||||
container.appendChild(child);
|
||||
// TODO: implement "gestures" support ?
|
||||
let uses = representation.uses || [];
|
||||
let promises = [];
|
||||
@@ -73,10 +64,9 @@ define([
|
||||
uses.forEach(function (key, i) {
|
||||
scope[key] = results[i];
|
||||
});
|
||||
element = openmct.$angular.element(child)
|
||||
templateLinker.link(
|
||||
scope,
|
||||
element,
|
||||
openmct.$angular.element(container),
|
||||
representation
|
||||
);
|
||||
container.style.height = '100%';
|
||||
@@ -93,11 +83,7 @@ define([
|
||||
}
|
||||
},
|
||||
destroy: function () {
|
||||
element.off();
|
||||
element.remove();
|
||||
scope.$destroy();
|
||||
element = null;
|
||||
scope = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,11 @@ export default class Editor extends EventEmitter {
|
||||
super();
|
||||
this.editing = false;
|
||||
this.openmct = openmct;
|
||||
document.addEventListener('drop', (event) => {
|
||||
if (!this.isEditing()) {
|
||||
this.edit();
|
||||
}
|
||||
}, {capture: true});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,11 +79,9 @@ export default class Editor extends EventEmitter {
|
||||
* @private
|
||||
*/
|
||||
cancel() {
|
||||
let cancelPromise = this.getTransactionService().cancel();
|
||||
this.getTransactionService().cancel();
|
||||
this.editing = false;
|
||||
this.emit('isEditing', false);
|
||||
|
||||
return cancelPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -98,24 +98,18 @@ define([
|
||||
composition.reorder(1, 0);
|
||||
let newComposition =
|
||||
publicAPI.objects.mutate.calls.mostRecent().args[2];
|
||||
let reorderPlan = listener.calls.mostRecent().args[0][0];
|
||||
|
||||
expect(reorderPlan.oldIndex).toBe(1);
|
||||
expect(reorderPlan.newIndex).toBe(0);
|
||||
expect(listener).toHaveBeenCalledWith(1, 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(reorderPlan.oldIndex).toBe(0);
|
||||
expect(reorderPlan.newIndex).toBe(2);
|
||||
expect(newComposition[0].key).toEqual('b');
|
||||
expect(newComposition[1].key).toEqual('c');
|
||||
expect(listener).toHaveBeenCalledWith(0, 2);
|
||||
expect(newComposition[0].key).toEqual('c');
|
||||
expect(newComposition[2].key).toEqual('a');
|
||||
})
|
||||
});
|
||||
|
||||
@@ -25,6 +25,7 @@ define([
|
||||
], function (
|
||||
_
|
||||
) {
|
||||
|
||||
/**
|
||||
* A CompositionCollection represents the list of domain objects contained
|
||||
* by another domain object. It provides methods for loading this
|
||||
@@ -62,6 +63,7 @@ define([
|
||||
this.onProviderRemove = this.onProviderRemove.bind(this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Listen for changes to this composition. Supports 'add', 'remove', and
|
||||
* 'load' events.
|
||||
@@ -74,11 +76,7 @@ define([
|
||||
if (!this.listeners[event]) {
|
||||
throw new Error('Event not supported by composition: ' + event);
|
||||
}
|
||||
if (!this.mutationListener) {
|
||||
this.mutationListener = this.publicAPI.objects.observe(this.domainObject, '*', (newDomainObject) => {
|
||||
this.domainObject = newDomainObject;
|
||||
})
|
||||
}
|
||||
|
||||
if (this.provider.on && this.provider.off) {
|
||||
if (event === 'add') {
|
||||
this.provider.on(
|
||||
@@ -134,10 +132,6 @@ define([
|
||||
|
||||
this.listeners[event].splice(index, 1);
|
||||
if (this.listeners[event].length === 0) {
|
||||
if (this.mutationListener) {
|
||||
this.mutationListener();
|
||||
delete this.mutationListener;
|
||||
}
|
||||
// Remove provider listener if this is the last callback to
|
||||
// be removed.
|
||||
if (this.provider.off && this.provider.on) {
|
||||
@@ -242,15 +236,19 @@ define([
|
||||
* @name remove
|
||||
*/
|
||||
CompositionCollection.prototype.reorder = function (oldIndex, newIndex, skipMutate) {
|
||||
this.provider.reorder(this.domainObject, oldIndex, newIndex);
|
||||
if (!skipMutate) {
|
||||
this.provider.reorder(this.domainObject, oldIndex, newIndex);
|
||||
} else {
|
||||
this.emit('reorder', oldIndex, newIndex);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle reorder from provider.
|
||||
* @private
|
||||
*/
|
||||
CompositionCollection.prototype.onProviderReorder = function (reorderMap) {
|
||||
this.emit('reorder', reorderMap);
|
||||
CompositionCollection.prototype.onProviderReorder = function (oldIndex, newIndex) {
|
||||
this.reorder(oldIndex, newIndex, true);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -206,32 +206,8 @@ define([
|
||||
|
||||
DefaultCompositionProvider.prototype.reorder = function (domainObject, oldIndex, newIndex) {
|
||||
let newComposition = domainObject.composition.slice();
|
||||
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
|
||||
});
|
||||
}
|
||||
}
|
||||
newComposition[newIndex] = domainObject.composition[oldIndex];
|
||||
newComposition[oldIndex] = domainObject.composition[newIndex];
|
||||
this.publicAPI.objects.mutate(domainObject, 'composition', newComposition);
|
||||
|
||||
let id = objectUtils.makeKeyString(domainObject.identifier);
|
||||
@@ -245,9 +221,9 @@ define([
|
||||
|
||||
function notify(listener) {
|
||||
if (listener.context) {
|
||||
listener.callback.call(listener.context, reorderPlan);
|
||||
listener.callback.call(listener.context, oldIndex, newIndex);
|
||||
} else {
|
||||
listener.callback(reorderPlan);
|
||||
listener.callback(oldIndex, newIndex);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -21,10 +21,8 @@
|
||||
*****************************************************************************/
|
||||
|
||||
define([
|
||||
'./object-utils.js',
|
||||
'lodash'
|
||||
], function (
|
||||
utils,
|
||||
_
|
||||
) {
|
||||
var ANY_OBJECT_EVENT = "mutation";
|
||||
@@ -43,9 +41,7 @@ define([
|
||||
}
|
||||
|
||||
function qualifiedEventName(object, eventName) {
|
||||
var keystring = utils.makeKeyString(object.identifier);
|
||||
|
||||
return [keystring, eventName].join(':');
|
||||
return [object.identifier.key, eventName].join(':');
|
||||
}
|
||||
|
||||
MutableObject.prototype.stopListening = function () {
|
||||
|
||||
@@ -226,20 +226,7 @@ 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.
|
||||
|
||||
@@ -27,16 +27,10 @@
|
||||
<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;
|
||||
@@ -64,44 +58,7 @@
|
||||
&__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>
|
||||
|
||||
@@ -56,11 +56,9 @@
|
||||
}
|
||||
|
||||
&__close-button {
|
||||
$p: $interiorMargin;
|
||||
$p: $interiorMarginSm;
|
||||
border-radius: 100% !important;
|
||||
color: $overlayColorFg;
|
||||
display: inline-block;
|
||||
font-size: 1.25em;
|
||||
position: absolute;
|
||||
top: $p; right: $p;
|
||||
}
|
||||
@@ -92,7 +90,6 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1 1 auto;
|
||||
height: 0; // Chrome 73 overflow bug fix
|
||||
overflow: auto;
|
||||
padding-right: $interiorMargin; // fend off scroll bar
|
||||
}
|
||||
@@ -154,7 +151,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.t-dialog-sm .l-overlay-small, // Legacy dialog support
|
||||
.l-overlay-fit {
|
||||
.c-overlay__outer {
|
||||
@include overlaySizing(auto);
|
||||
|
||||
@@ -280,11 +280,7 @@ define([
|
||||
if (!provider) {
|
||||
return Promise.reject('No provider found');
|
||||
}
|
||||
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);
|
||||
});
|
||||
return provider.request.apply(provider, arguments);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -66,11 +66,10 @@ export default {
|
||||
|
||||
this.items.splice(index, 1);
|
||||
},
|
||||
reorder(reorderPlan) {
|
||||
let oldItems = this.items.slice();
|
||||
reorderPlan.forEach((reorderEvent) => {
|
||||
this.$set(this.items, reorderEvent.newIndex, oldItems[reorderEvent.oldIndex]);
|
||||
});
|
||||
reorder(oldIndex, newIndex) {
|
||||
let objectAtOldIndex = this.items[oldIndex];
|
||||
this.$set(this.items, oldIndex, this.items[newIndex]);
|
||||
this.$set(this.items, newIndex, objectAtOldIndex);
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
||||
@@ -93,11 +93,11 @@
|
||||
this.primaryTelemetryObjects.splice(index,1);
|
||||
primary = undefined;
|
||||
},
|
||||
reorderPrimary(reorderPlan) {
|
||||
let oldComposition = this.primaryTelemetryObjects.slice();
|
||||
reorderPlan.forEach(reorderEvent => {
|
||||
this.$set(this.primaryTelemetryObjects, reorderEvent.newIndex, oldComposition[reorderEvent.oldIndex]);
|
||||
});
|
||||
reorderPrimary(oldIndex, newIndex) {
|
||||
let objectAtOldIndex = this.primaryTelemetryObjects[oldIndex];
|
||||
this.$set(this.primaryTelemetryObjects, oldIndex, this.primaryTelemetryObjects[newIndex]);
|
||||
this.$set(this.primaryTelemetryObjects, newIndex, objectAtOldIndex);
|
||||
|
||||
},
|
||||
addSecondary(primary) {
|
||||
return (domainObject) => {
|
||||
|
||||
@@ -28,17 +28,11 @@ define([], function () {
|
||||
key: "layout",
|
||||
description: "A toolbar for objects inside a display layout.",
|
||||
forSelection: function (selection) {
|
||||
if (!selection || selection.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let selectionPath = selection[0];
|
||||
let selectedObject = selectionPath[0];
|
||||
let selectedParent = selectionPath[1];
|
||||
|
||||
// Apply the layout toolbar if the selected object is inside a layout, or the main layout is selected.
|
||||
return (selectedParent && selectedParent.context.item && selectedParent.context.item.type === 'layout') ||
|
||||
(selectedObject.context.item && selectedObject.context.item.type === 'layout');
|
||||
// Apply the layout toolbar if the selected object
|
||||
// is inside a layout, or the main layout is selected.
|
||||
return (selection &&
|
||||
((selection[1] && selection[1].context.item && selection[1].context.item.type === 'layout') ||
|
||||
(selection[0].context.item && selection[0].context.item.type === 'layout')));
|
||||
},
|
||||
toolbar: function (selection) {
|
||||
const DIALOG_FORM = {
|
||||
@@ -79,72 +73,190 @@ define([], function () {
|
||||
return openmct.$injector.get('dialogService').getUserInput(form, {});
|
||||
}
|
||||
|
||||
function getPath(selectionPath) {
|
||||
return `configuration.items[${selectionPath[0].context.index}]`;
|
||||
function getPath() {
|
||||
return `configuration.items[${selection[0].context.index}]`;
|
||||
}
|
||||
|
||||
function getAllTypes(selection) {
|
||||
return selection.filter(selectionPath => {
|
||||
let type = selectionPath[0].context.layoutItem.type;
|
||||
return type === 'text-view' ||
|
||||
type === 'telemetry-view' ||
|
||||
type === 'box-view' ||
|
||||
type === 'image-view' ||
|
||||
type === 'line-view' ||
|
||||
type === 'subobject-view';
|
||||
let selectedParent = selection[1] && selection[1].context.item,
|
||||
selectedObject = selection[0].context.item,
|
||||
layoutItem = selection[0].context.layoutItem,
|
||||
toolbar = [];
|
||||
|
||||
if (selectedObject && selectedObject.type === 'layout') {
|
||||
toolbar.push({
|
||||
control: "menu",
|
||||
domainObject: selectedObject,
|
||||
method: function (option) {
|
||||
let name = option.name.toLowerCase();
|
||||
let form = DIALOG_FORM[name];
|
||||
if (form) {
|
||||
getUserInput(form)
|
||||
.then(element => selection[0].context.addElement(name, element));
|
||||
} else {
|
||||
selection[0].context.addElement(name);
|
||||
}
|
||||
},
|
||||
key: "add",
|
||||
icon: "icon-plus",
|
||||
label: "Add",
|
||||
options: [
|
||||
{
|
||||
"name": "Box",
|
||||
"class": "icon-box-round-corners"
|
||||
},
|
||||
{
|
||||
"name": "Line",
|
||||
"class": "icon-line-horz"
|
||||
},
|
||||
{
|
||||
"name": "Text",
|
||||
"class": "icon-font"
|
||||
},
|
||||
{
|
||||
"name": "Image",
|
||||
"class": "icon-image"
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
function getAddButton(selection, selectionPath) {
|
||||
if (selection.length === 1) {
|
||||
selectionPath = selectionPath || selection[0];
|
||||
return {
|
||||
control: "menu",
|
||||
domainObject: selectionPath[0].context.item,
|
||||
method: function (option) {
|
||||
let name = option.name.toLowerCase();
|
||||
let form = DIALOG_FORM[name];
|
||||
if (form) {
|
||||
getUserInput(form)
|
||||
.then(element => selectionPath[0].context.addElement(name, element));
|
||||
} else {
|
||||
selectionPath[0].context.addElement(name);
|
||||
}
|
||||
},
|
||||
key: "add",
|
||||
icon: "icon-plus",
|
||||
label: "Add",
|
||||
options: [
|
||||
{
|
||||
"name": "Box",
|
||||
"class": "icon-box-round-corners"
|
||||
},
|
||||
{
|
||||
"name": "Line",
|
||||
"class": "icon-line-horz"
|
||||
},
|
||||
{
|
||||
"name": "Text",
|
||||
"class": "icon-font"
|
||||
},
|
||||
{
|
||||
"name": "Image",
|
||||
"class": "icon-image"
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
if (!layoutItem) {
|
||||
return toolbar;
|
||||
}
|
||||
|
||||
function getToggleFrameButton(selectedParent, selection) {
|
||||
return {
|
||||
let separator = {
|
||||
control: "separator"
|
||||
};
|
||||
let remove = {
|
||||
control: "button",
|
||||
domainObject: selectedParent,
|
||||
icon: "icon-trash",
|
||||
title: "Delete the selected object",
|
||||
method: function () {
|
||||
let removeItem = selection[1].context.removeItem;
|
||||
let prompt = openmct.overlays.dialog({
|
||||
iconClass: 'alert',
|
||||
message: `Warning! This action will remove this item from the Display Layout. Do you want to continue?`,
|
||||
buttons: [
|
||||
{
|
||||
label: 'Ok',
|
||||
emphasis: 'true',
|
||||
callback: function () {
|
||||
removeItem(layoutItem, selection[0].context.index);
|
||||
prompt.dismiss();
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Cancel',
|
||||
callback: function () {
|
||||
prompt.dismiss();
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
};
|
||||
let stackOrder = {
|
||||
control: "menu",
|
||||
domainObject: selectedParent,
|
||||
icon: "icon-layers",
|
||||
title: "Move the selected object above or below other objects",
|
||||
options: [
|
||||
{
|
||||
name: "Move to Top",
|
||||
value: "top",
|
||||
class: "icon-arrow-double-up"
|
||||
},
|
||||
{
|
||||
name: "Move Up",
|
||||
value: "up",
|
||||
class: "icon-arrow-up"
|
||||
},
|
||||
{
|
||||
name: "Move Down",
|
||||
value: "down",
|
||||
class: "icon-arrow-down"
|
||||
},
|
||||
{
|
||||
name: "Move to Bottom",
|
||||
value: "bottom",
|
||||
class: "icon-arrow-double-down"
|
||||
}
|
||||
],
|
||||
method: function (option) {
|
||||
selection[1].context.orderItem(option.value, selection[0].context.index);
|
||||
}
|
||||
};
|
||||
let useGrid = {
|
||||
control: "toggle-button",
|
||||
domainObject: selectedParent,
|
||||
property: function () {
|
||||
return getPath() + ".useGrid";
|
||||
},
|
||||
options: [
|
||||
{
|
||||
value: false,
|
||||
icon: "icon-grid-snap-to",
|
||||
title: "Grid snapping enabled"
|
||||
},
|
||||
{
|
||||
value: true,
|
||||
icon: "icon-grid-snap-no",
|
||||
title: "Grid snapping disabled"
|
||||
}
|
||||
]
|
||||
};
|
||||
let x = {
|
||||
control: "input",
|
||||
type: "number",
|
||||
domainObject: selectedParent,
|
||||
property: function () {
|
||||
return getPath() + ".x";
|
||||
},
|
||||
label: "X:",
|
||||
title: "X position"
|
||||
},
|
||||
y = {
|
||||
control: "input",
|
||||
type: "number",
|
||||
domainObject: selectedParent,
|
||||
property: function () {
|
||||
return getPath() + ".y";
|
||||
},
|
||||
label: "Y:",
|
||||
title: "Y position",
|
||||
},
|
||||
width = {
|
||||
control: 'input',
|
||||
type: 'number',
|
||||
domainObject: selectedParent,
|
||||
property: function () {
|
||||
return getPath() + ".width";
|
||||
},
|
||||
label: 'W:',
|
||||
title: 'Resize object width'
|
||||
},
|
||||
height = {
|
||||
control: 'input',
|
||||
type: 'number',
|
||||
domainObject: selectedParent,
|
||||
property: function () {
|
||||
return getPath() + ".height";
|
||||
},
|
||||
label: 'H:',
|
||||
title: 'Resize object height'
|
||||
};
|
||||
|
||||
if (layoutItem.type === 'subobject-view') {
|
||||
if (toolbar.length > 0) {
|
||||
toolbar.push(separator);
|
||||
}
|
||||
|
||||
toolbar.push({
|
||||
control: "toggle-button",
|
||||
domainObject: selectedParent,
|
||||
applicableSelectedItems: selection.filter(selectionPath =>
|
||||
selectionPath[0].context.layoutItem.type === 'subobject-view'
|
||||
),
|
||||
property: function (selectionPath) {
|
||||
return getPath(selectionPath) + ".hasFrame";
|
||||
property: function () {
|
||||
return getPath() + ".hasFrame";
|
||||
},
|
||||
options: [
|
||||
{
|
||||
@@ -158,186 +270,52 @@ define([], function () {
|
||||
title: "Frame hidden"
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
function getRemoveButton(selectedParent, selectionPath, selection) {
|
||||
return {
|
||||
control: "button",
|
||||
domainObject: selectedParent,
|
||||
icon: "icon-trash",
|
||||
title: "Delete the selected object",
|
||||
method: function () {
|
||||
let removeItem = selectionPath[1].context.removeItem;
|
||||
let prompt = openmct.overlays.dialog({
|
||||
iconClass: 'alert',
|
||||
message: `Warning! This action will remove this item from the Display Layout. Do you want to continue?`,
|
||||
buttons: [
|
||||
{
|
||||
label: 'Ok',
|
||||
emphasis: 'true',
|
||||
callback: function () {
|
||||
removeItem(getAllTypes(selection));
|
||||
prompt.dismiss();
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Cancel',
|
||||
callback: function () {
|
||||
prompt.dismiss();
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function getStackOrder(selectedParent, selectionPath) {
|
||||
return {
|
||||
control: "menu",
|
||||
domainObject: selectedParent,
|
||||
icon: "icon-layers",
|
||||
title: "Move the selected object above or below other objects",
|
||||
options: [
|
||||
{
|
||||
name: "Move to Top",
|
||||
value: "top",
|
||||
class: "icon-arrow-double-up"
|
||||
},
|
||||
{
|
||||
name: "Move Up",
|
||||
value: "up",
|
||||
class: "icon-arrow-up"
|
||||
},
|
||||
{
|
||||
name: "Move Down",
|
||||
value: "down",
|
||||
class: "icon-arrow-down"
|
||||
},
|
||||
{
|
||||
name: "Move to Bottom",
|
||||
value: "bottom",
|
||||
class: "icon-arrow-double-down"
|
||||
}
|
||||
],
|
||||
method: function (option) {
|
||||
selectionPath[1].context.orderItem(option.value, getAllTypes(selection));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function getXInput(selectedParent, selection) {
|
||||
if (selection.length === 1) {
|
||||
return {
|
||||
control: "input",
|
||||
type: "number",
|
||||
domainObject: selectedParent,
|
||||
applicableSelectedItems: getAllTypes(selection),
|
||||
property: function (selectionPath) {
|
||||
return getPath(selectionPath) + ".x";
|
||||
},
|
||||
label: "X:",
|
||||
title: "X position"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function getYInput(selectedParent, selection) {
|
||||
if (selection.length === 1) {
|
||||
return {
|
||||
control: "input",
|
||||
type: "number",
|
||||
domainObject: selectedParent,
|
||||
applicableSelectedItems: getAllTypes(selection),
|
||||
property: function (selectionPath) {
|
||||
return getPath(selectionPath) + ".y";
|
||||
},
|
||||
label: "Y:",
|
||||
title: "Y position",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function getWidthInput(selectedParent, selection) {
|
||||
if (selection.length === 1) {
|
||||
return {
|
||||
control: 'input',
|
||||
type: 'number',
|
||||
domainObject: selectedParent,
|
||||
applicableSelectedItems: getAllTypes(selection),
|
||||
property: function (selectionPath) {
|
||||
return getPath(selectionPath) + ".width";
|
||||
},
|
||||
label: 'W:',
|
||||
title: 'Resize object width'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function getHeightInput(selectedParent, selection) {
|
||||
if (selection.length === 1) {
|
||||
return {
|
||||
control: 'input',
|
||||
type: 'number',
|
||||
domainObject: selectedParent,
|
||||
applicableSelectedItems: getAllTypes(selection),
|
||||
property: function (selectionPath) {
|
||||
return getPath(selectionPath) + ".height";
|
||||
},
|
||||
label: 'H:',
|
||||
title: 'Resize object height'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function getX2Input(selectedParent, selection) {
|
||||
if (selection.length === 1) {
|
||||
return {
|
||||
control: "input",
|
||||
type: "number",
|
||||
domainObject: selectedParent,
|
||||
applicableSelectedItems: selection.filter(selectionPath => {
|
||||
return selectionPath[0].context.layoutItem.type === 'line-view';
|
||||
}),
|
||||
property: function (selectionPath) {
|
||||
return getPath(selectionPath) + ".x2";
|
||||
},
|
||||
label: "X2:",
|
||||
title: "X2 position"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function getY2Input(selectedParent, selection) {
|
||||
if (selection.length === 1) {
|
||||
return {
|
||||
control: "input",
|
||||
type: "number",
|
||||
domainObject: selectedParent,
|
||||
applicableSelectedItems: selection.filter(selectionPath => {
|
||||
return selectionPath[0].context.layoutItem.type === 'line-view';
|
||||
}),
|
||||
property: function (selectionPath) {
|
||||
return getPath(selectionPath) + ".y2";
|
||||
},
|
||||
label: "Y2:",
|
||||
title: "Y2 position",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function getTextSizeMenu(selectedParent, selection) {
|
||||
});
|
||||
toolbar.push(separator);
|
||||
toolbar.push(stackOrder);
|
||||
toolbar.push(x);
|
||||
toolbar.push(y);
|
||||
toolbar.push(width);
|
||||
toolbar.push(height);
|
||||
toolbar.push(useGrid);
|
||||
toolbar.push(separator);
|
||||
toolbar.push(remove);
|
||||
} else {
|
||||
const TEXT_SIZE = [8, 9, 10, 11, 12, 13, 14, 15, 16, 20, 24, 30, 36, 48, 72, 96, 128];
|
||||
return {
|
||||
let fill = {
|
||||
control: "color-picker",
|
||||
domainObject: selectedParent,
|
||||
property: function () {
|
||||
return getPath() + ".fill";
|
||||
},
|
||||
icon: "icon-paint-bucket",
|
||||
title: "Set fill color"
|
||||
},
|
||||
stroke = {
|
||||
control: "color-picker",
|
||||
domainObject: selectedParent,
|
||||
property: function () {
|
||||
return getPath() + ".stroke";
|
||||
},
|
||||
icon: "icon-line-horz",
|
||||
title: "Set border color"
|
||||
},
|
||||
color = {
|
||||
control: "color-picker",
|
||||
domainObject: selectedParent,
|
||||
property: function () {
|
||||
return getPath() + ".color";
|
||||
},
|
||||
icon: "icon-font",
|
||||
mandatory: true,
|
||||
title: "Set text color",
|
||||
preventNone: true
|
||||
},
|
||||
size = {
|
||||
control: "select-menu",
|
||||
domainObject: selectedParent,
|
||||
applicableSelectedItems: selection.filter(selectionPath => {
|
||||
let type = selectionPath[0].context.layoutItem.type;
|
||||
return type === 'text-view' || type === 'telemetry-view';
|
||||
}),
|
||||
property: function (selectionPath) {
|
||||
return getPath(selectionPath) + ".size";
|
||||
property: function () {
|
||||
return getPath() + ".size";
|
||||
},
|
||||
title: "Set text size",
|
||||
options: TEXT_SIZE.map(size => {
|
||||
@@ -346,128 +324,13 @@ define([], function () {
|
||||
};
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
function getFillMenu(selectedParent, selection) {
|
||||
return {
|
||||
control: "color-picker",
|
||||
domainObject: selectedParent,
|
||||
applicableSelectedItems: selection.filter(selectionPath => {
|
||||
let type = selectionPath[0].context.layoutItem.type;
|
||||
return type === 'text-view' ||
|
||||
type === 'telemetry-view' ||
|
||||
type === 'box-view';
|
||||
}),
|
||||
property: function (selectionPath) {
|
||||
return getPath(selectionPath) + ".fill";
|
||||
},
|
||||
icon: "icon-paint-bucket",
|
||||
title: "Set fill color"
|
||||
};
|
||||
}
|
||||
|
||||
function getStrokeMenu(selectedParent, selection) {
|
||||
return {
|
||||
control: "color-picker",
|
||||
domainObject: selectedParent,
|
||||
applicableSelectedItems: selection.filter(selectionPath => {
|
||||
let type = selectionPath[0].context.layoutItem.type;
|
||||
return type === 'text-view' ||
|
||||
type === 'telemetry-view' ||
|
||||
type === 'box-view' ||
|
||||
type === 'image-view' ||
|
||||
type === 'line-view';
|
||||
}),
|
||||
property: function (selectionPath) {
|
||||
return getPath(selectionPath) + ".stroke";
|
||||
},
|
||||
icon: "icon-line-horz",
|
||||
title: "Set border color"
|
||||
};
|
||||
}
|
||||
|
||||
function getTextColorMenu(selectedParent, selection) {
|
||||
return {
|
||||
control: "color-picker",
|
||||
domainObject: selectedParent,
|
||||
applicableSelectedItems: selection.filter(selectionPath => {
|
||||
let type = selectionPath[0].context.layoutItem.type;
|
||||
return type === 'text-view' || type === 'telemetry-view';
|
||||
}),
|
||||
property: function (selectionPath) {
|
||||
return getPath(selectionPath) + ".color";
|
||||
},
|
||||
icon: "icon-font",
|
||||
mandatory: true,
|
||||
title: "Set text color",
|
||||
preventNone: true
|
||||
};
|
||||
}
|
||||
|
||||
function getURLButton(selectedParent, selection) {
|
||||
return {
|
||||
control: "button",
|
||||
domainObject: selectedParent,
|
||||
applicableSelectedItems: selection.filter(selectionPath => {
|
||||
return selectionPath[0].context.layoutItem.type === 'image-view';
|
||||
}),
|
||||
property: function (selectionPath) {
|
||||
return getPath(selectionPath);
|
||||
},
|
||||
icon: "icon-image",
|
||||
title: "Edit image properties",
|
||||
dialog: DIALOG_FORM['image']
|
||||
};
|
||||
}
|
||||
|
||||
function getTextButton(selectedParent, selection) {
|
||||
return {
|
||||
control: "button",
|
||||
domainObject: selectedParent,
|
||||
applicableSelectedItems: selection.filter(selectionPath => {
|
||||
return selectionPath[0].context.layoutItem.type === 'text-view';
|
||||
}),
|
||||
property: function (selectionPath) {
|
||||
return getPath(selectionPath);
|
||||
},
|
||||
icon: "icon-gear",
|
||||
title: "Edit text properties",
|
||||
dialog: DIALOG_FORM['text']
|
||||
};
|
||||
}
|
||||
|
||||
function getTelemetryValueMenu(selectionPath, selection) {
|
||||
if (selection.length === 1) {
|
||||
return {
|
||||
control: "select-menu",
|
||||
domainObject: selectionPath[1].context.item,
|
||||
applicableSelectedItems: selection.filter(selectionPath => {
|
||||
return selectionPath[0].context.layoutItem.type === 'telemetry-view';
|
||||
}),
|
||||
property: function (selectionPath) {
|
||||
return getPath(selectionPath) + ".value";
|
||||
},
|
||||
title: "Set value",
|
||||
options: openmct.telemetry.getMetadata(selectionPath[0].context.item).values().map(value => {
|
||||
return {
|
||||
name: value.name,
|
||||
value: value.key
|
||||
}
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function getDisplayModeMenu(selectedParent, selection) {
|
||||
if (selection.length === 1) {
|
||||
return {
|
||||
if (layoutItem.type === 'telemetry-view') {
|
||||
let displayMode = {
|
||||
control: "select-menu",
|
||||
domainObject: selectedParent,
|
||||
applicableSelectedItems: selection.filter(selectionPath => {
|
||||
return selectionPath[0].context.layoutItem.type === 'telemetry-view';
|
||||
}),
|
||||
property: function (selectionPath) {
|
||||
return getPath(selectionPath) + ".displayMode";
|
||||
property: function () {
|
||||
return getPath() + ".displayMode";
|
||||
},
|
||||
title: "Set display mode",
|
||||
options: [
|
||||
@@ -484,196 +347,146 @@ define([], function () {
|
||||
value: "value"
|
||||
}
|
||||
]
|
||||
},
|
||||
value = {
|
||||
control: "select-menu",
|
||||
domainObject: selectedParent,
|
||||
property: function () {
|
||||
return getPath() + ".value";
|
||||
},
|
||||
title: "Set value",
|
||||
options: openmct.telemetry.getMetadata(selectedObject).values().map(value => {
|
||||
return {
|
||||
name: value.name,
|
||||
value: value.key
|
||||
}
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function getSeparator() {
|
||||
return {
|
||||
control: "separator"
|
||||
};
|
||||
}
|
||||
|
||||
function isMainLayoutSelected(selectionPath) {
|
||||
let selectedObject = selectionPath[0].context.item;
|
||||
return selectedObject && selectedObject.type === 'layout' &&
|
||||
!selectionPath[0].context.layoutItem;
|
||||
}
|
||||
|
||||
if (isMainLayoutSelected(selection[0])) {
|
||||
return [getAddButton(selection)];
|
||||
}
|
||||
|
||||
let toolbar = {
|
||||
'add-menu': [],
|
||||
'toggle-frame': [],
|
||||
'display-mode': [],
|
||||
'telemetry-value': [],
|
||||
'style': [],
|
||||
'text-style': [],
|
||||
'position': [],
|
||||
'text': [],
|
||||
'url': [],
|
||||
'remove': [],
|
||||
};
|
||||
|
||||
selection.forEach(selectionPath => {
|
||||
let selectedParent = selectionPath[1].context.item;
|
||||
let layoutItem = selectionPath[0].context.layoutItem;
|
||||
|
||||
if (layoutItem.type === 'subobject-view') {
|
||||
if (toolbar['add-menu'].length === 0 && selectionPath[0].context.item.type === 'layout') {
|
||||
toolbar['add-menu'] = [getAddButton(selection, selectionPath)];
|
||||
}
|
||||
if (toolbar['toggle-frame'].length === 0) {
|
||||
toolbar['toggle-frame'] = [getToggleFrameButton(selectedParent, selection)];
|
||||
}
|
||||
if (toolbar['position'].length === 0) {
|
||||
toolbar['position'] = [
|
||||
getStackOrder(selectedParent, selectionPath),
|
||||
getXInput(selectedParent, selection),
|
||||
getYInput(selectedParent, selection),
|
||||
getHeightInput(selectedParent, selection),
|
||||
getWidthInput(selectedParent, selection)
|
||||
];
|
||||
}
|
||||
if (toolbar['remove'].length === 0) {
|
||||
toolbar['remove'] = [getRemoveButton(selectedParent, selectionPath, selection)];
|
||||
}
|
||||
} else if (layoutItem.type === 'telemetry-view') {
|
||||
if (toolbar['display-mode'].length === 0) {
|
||||
toolbar['display-mode'] = [getDisplayModeMenu(selectedParent, selection)];
|
||||
}
|
||||
if (toolbar['telemetry-value'].length === 0) {
|
||||
toolbar['telemetry-value'] = [getTelemetryValueMenu(selectionPath, selection)];
|
||||
}
|
||||
if (toolbar['style'].length < 2) {
|
||||
toolbar['style'] = [
|
||||
getFillMenu(selectedParent, selection),
|
||||
getStrokeMenu(selectedParent, selection)
|
||||
];
|
||||
}
|
||||
if (toolbar['text-style'].length === 0) {
|
||||
toolbar['text-style'] = [
|
||||
getTextColorMenu(selectedParent, selection),
|
||||
getTextSizeMenu(selectedParent, selection)
|
||||
];
|
||||
}
|
||||
if (toolbar['position'].length === 0) {
|
||||
toolbar['position'] = [
|
||||
getStackOrder(selectedParent, selectionPath),
|
||||
getXInput(selectedParent, selection),
|
||||
getYInput(selectedParent, selection),
|
||||
getHeightInput(selectedParent, selection),
|
||||
getWidthInput(selectedParent, selection)
|
||||
];
|
||||
}
|
||||
if (toolbar['remove'].length === 0) {
|
||||
toolbar['remove'] = [getRemoveButton(selectedParent, selectionPath, selection)];
|
||||
}
|
||||
toolbar = [
|
||||
displayMode,
|
||||
separator,
|
||||
value,
|
||||
separator,
|
||||
fill,
|
||||
stroke,
|
||||
color,
|
||||
separator,
|
||||
size,
|
||||
separator,
|
||||
stackOrder,
|
||||
x,
|
||||
y,
|
||||
height,
|
||||
width,
|
||||
useGrid,
|
||||
separator,
|
||||
remove
|
||||
];
|
||||
} else if (layoutItem.type === 'text-view') {
|
||||
if (toolbar['style'].length < 2) {
|
||||
toolbar['style'] = [
|
||||
getFillMenu(selectedParent, selection),
|
||||
getStrokeMenu(selectedParent, selection)
|
||||
];
|
||||
}
|
||||
if (toolbar['text-style'].length === 0) {
|
||||
toolbar['text-style'] = [
|
||||
getTextColorMenu(selectedParent, selection),
|
||||
getTextSizeMenu(selectedParent, selection)
|
||||
];
|
||||
}
|
||||
if (toolbar['position'].length === 0) {
|
||||
toolbar['position'] = [
|
||||
getStackOrder(selectedParent, selectionPath),
|
||||
getXInput(selectedParent, selection),
|
||||
getYInput(selectedParent, selection),
|
||||
getHeightInput(selectedParent, selection),
|
||||
getWidthInput(selectedParent, selection)
|
||||
];
|
||||
}
|
||||
if (toolbar['text'].length === 0) {
|
||||
toolbar['text'] = [getTextButton(selectedParent, selection)];
|
||||
}
|
||||
if (toolbar['remove'].length === 0) {
|
||||
toolbar['remove'] = [getRemoveButton(selectedParent, selectionPath, selection)];
|
||||
}
|
||||
let text = {
|
||||
control: "button",
|
||||
domainObject: selectedParent,
|
||||
property: function () {
|
||||
return getPath();
|
||||
},
|
||||
icon: "icon-gear",
|
||||
title: "Edit text properties",
|
||||
dialog: DIALOG_FORM['text']
|
||||
};
|
||||
toolbar = [
|
||||
fill,
|
||||
stroke,
|
||||
separator,
|
||||
color,
|
||||
size,
|
||||
separator,
|
||||
stackOrder,
|
||||
x,
|
||||
y,
|
||||
height,
|
||||
width,
|
||||
useGrid,
|
||||
separator,
|
||||
text,
|
||||
separator,
|
||||
remove
|
||||
];
|
||||
} else if (layoutItem.type === 'box-view') {
|
||||
if (toolbar['style'].length < 2) {
|
||||
toolbar['style'] = [
|
||||
getFillMenu(selectedParent, selection),
|
||||
getStrokeMenu(selectedParent, selection)
|
||||
];
|
||||
}
|
||||
if (toolbar['position'].length === 0) {
|
||||
toolbar['position'] = [
|
||||
getStackOrder(selectedParent, selectionPath),
|
||||
getXInput(selectedParent, selection),
|
||||
getYInput(selectedParent, selection),
|
||||
getHeightInput(selectedParent, selection),
|
||||
getWidthInput(selectedParent, selection)
|
||||
];
|
||||
}
|
||||
if (toolbar['remove'].length === 0) {
|
||||
toolbar['remove'] = [getRemoveButton(selectedParent, selectionPath, selection)];
|
||||
}
|
||||
toolbar = [
|
||||
fill,
|
||||
stroke,
|
||||
separator,
|
||||
stackOrder,
|
||||
x,
|
||||
y,
|
||||
height,
|
||||
width,
|
||||
useGrid,
|
||||
separator,
|
||||
remove
|
||||
];
|
||||
} else if (layoutItem.type === 'image-view') {
|
||||
if (toolbar['style'].length === 0) {
|
||||
toolbar['style'] = [
|
||||
getStrokeMenu(selectedParent, selection)
|
||||
];
|
||||
}
|
||||
if (toolbar['position'].length === 0) {
|
||||
toolbar['position'] = [
|
||||
getStackOrder(selectedParent, selectionPath),
|
||||
getXInput(selectedParent, selection),
|
||||
getYInput(selectedParent, selection),
|
||||
getHeightInput(selectedParent, selection),
|
||||
getWidthInput(selectedParent, selection)
|
||||
];
|
||||
}
|
||||
if (toolbar['url'].length === 0) {
|
||||
toolbar['url'] = [getURLButton(selectedParent, selection)];
|
||||
}
|
||||
if (toolbar['remove'].length === 0) {
|
||||
toolbar['remove'] = [getRemoveButton(selectedParent, selectionPath, selection)];
|
||||
}
|
||||
let url = {
|
||||
control: "button",
|
||||
domainObject: selectedParent,
|
||||
property: function () {
|
||||
return getPath();
|
||||
},
|
||||
icon: "icon-image",
|
||||
title: "Edit image properties",
|
||||
dialog: DIALOG_FORM['image']
|
||||
};
|
||||
toolbar = [
|
||||
stroke,
|
||||
separator,
|
||||
stackOrder,
|
||||
x,
|
||||
y,
|
||||
height,
|
||||
width,
|
||||
useGrid,
|
||||
separator,
|
||||
url,
|
||||
separator,
|
||||
remove
|
||||
];
|
||||
} else if (layoutItem.type === 'line-view') {
|
||||
if (toolbar['style'].length === 0) {
|
||||
toolbar['style'] = [
|
||||
getStrokeMenu(selectedParent, selection)
|
||||
];
|
||||
}
|
||||
if (toolbar['position'].length === 0) {
|
||||
toolbar['position'] = [
|
||||
getStackOrder(selectedParent, selectionPath),
|
||||
getXInput(selectedParent, selection),
|
||||
getYInput(selectedParent, selection),
|
||||
getX2Input(selectedParent, selection),
|
||||
getY2Input(selectedParent, selection)
|
||||
];
|
||||
}
|
||||
if (toolbar['remove'].length === 0) {
|
||||
toolbar['remove'] = [getRemoveButton(selectedParent, selectionPath, selection)];
|
||||
}
|
||||
let x2 = {
|
||||
control: "input",
|
||||
type: "number",
|
||||
domainObject: selectedParent,
|
||||
property: function () {
|
||||
return getPath() + ".x2";
|
||||
},
|
||||
label: "X2:",
|
||||
title: "X2 position"
|
||||
},
|
||||
y2 = {
|
||||
control: "input",
|
||||
type: "number",
|
||||
domainObject: selectedParent,
|
||||
property: function () {
|
||||
return getPath() + ".y2";
|
||||
},
|
||||
label: "Y2:",
|
||||
title: "Y2 position",
|
||||
};
|
||||
toolbar = [
|
||||
stroke,
|
||||
separator,
|
||||
stackOrder,
|
||||
x,
|
||||
y,
|
||||
x2,
|
||||
y2,
|
||||
useGrid,
|
||||
separator,
|
||||
remove
|
||||
];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let toolbarArray = Object.values(toolbar);
|
||||
return _.flatten(toolbarArray.reduce((accumulator, group, index) => {
|
||||
group = group.filter(control => control !== undefined);
|
||||
|
||||
if (group.length > 0) {
|
||||
accumulator.push(group);
|
||||
|
||||
if (index < toolbarArray.length - 1) {
|
||||
accumulator.push(getSeparator());
|
||||
}
|
||||
}
|
||||
|
||||
return accumulator;
|
||||
}, []));
|
||||
return toolbar;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ define(
|
||||
* @param {number[]} pixelDelta the offset from the
|
||||
* original position, in pixels
|
||||
*/
|
||||
LayoutDrag.prototype.getAdjustedPositionAndDimensions = function (pixelDelta) {
|
||||
LayoutDrag.prototype.getAdjustedPosition = function (pixelDelta) {
|
||||
var gridDelta = toGridDelta(this.gridSize, pixelDelta);
|
||||
return {
|
||||
position: max(add(
|
||||
@@ -109,16 +109,6 @@ define(
|
||||
};
|
||||
};
|
||||
|
||||
LayoutDrag.prototype.getAdjustedPosition = function (pixelDelta) {
|
||||
var gridDelta = toGridDelta(this.gridSize, pixelDelta);
|
||||
return {
|
||||
position: max(add(
|
||||
this.rawPosition.position,
|
||||
multiply(gridDelta, this.posFactor)
|
||||
), [0, 0])
|
||||
};
|
||||
};
|
||||
|
||||
return LayoutDrag;
|
||||
|
||||
}
|
||||
|
||||
@@ -23,8 +23,7 @@
|
||||
<template>
|
||||
<layout-frame :item="item"
|
||||
:grid-size="gridSize"
|
||||
@move="(gridDelta) => $emit('move', gridDelta)"
|
||||
@endMove="() => $emit('endMove')">
|
||||
@endDrag="(item, updates) => $emit('endDrag', item, updates)">
|
||||
<div class="c-box-view"
|
||||
:style="style">
|
||||
</div>
|
||||
@@ -55,7 +54,8 @@
|
||||
x: 1,
|
||||
y: 1,
|
||||
width: 10,
|
||||
height: 5
|
||||
height: 5,
|
||||
useGrid: true
|
||||
};
|
||||
},
|
||||
inject: ['openmct'],
|
||||
|
||||
@@ -23,11 +23,8 @@
|
||||
<template>
|
||||
<div class="l-layout"
|
||||
@dragover="handleDragOver"
|
||||
@click.capture="bypassSelection"
|
||||
@drop="handleDrop"
|
||||
:class="{
|
||||
'is-multi-selected': selectedLayoutItems.length > 1
|
||||
}">
|
||||
@click="bypassSelection"
|
||||
@drop="handleDrop">
|
||||
<!-- Background grid -->
|
||||
<div class="l-layout__grid-holder c-grid">
|
||||
<div class="c-grid__x l-grid l-grid-x"
|
||||
@@ -42,38 +39,18 @@
|
||||
:is="item.type"
|
||||
:item="item"
|
||||
:key="item.id"
|
||||
:gridSize="gridSize"
|
||||
:gridSize="item.useGrid ? gridSize : [1, 1]"
|
||||
:initSelect="initSelectIndex === index"
|
||||
:index="index"
|
||||
:multiSelect="selectedLayoutItems.length > 1"
|
||||
@move="move"
|
||||
@endMove="endMove"
|
||||
@endLineResize='endLineResize'>
|
||||
@endDrag="endDrag"
|
||||
>
|
||||
</component>
|
||||
<edit-marquee v-if='showMarquee'
|
||||
:gridSize="gridSize"
|
||||
:selectedLayoutItems="selectedLayoutItems"
|
||||
@endResize="endResize">
|
||||
</edit-marquee>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@import "~styles/sass-base";
|
||||
|
||||
@mixin displayMarquee($c) {
|
||||
> .c-frame-edit {
|
||||
// All other frames
|
||||
//@include test($c, 0.4);
|
||||
display: block;
|
||||
}
|
||||
> .c-frame > .c-frame-edit {
|
||||
// Line object frame
|
||||
//@include test($c, 0.4);
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.l-layout {
|
||||
@include abs();
|
||||
display: flex;
|
||||
@@ -93,7 +70,7 @@
|
||||
.l-shell__main-container {
|
||||
&[s-selected],
|
||||
&[s-selected-parent] {
|
||||
// Display grid and allow edit marquee to display in main layout holder when editing
|
||||
// Display grid in main layout holder when editing
|
||||
> .l-layout {
|
||||
background: $editUIGridColorBg;
|
||||
|
||||
@@ -107,7 +84,7 @@
|
||||
.l-layout__frame {
|
||||
&[s-selected],
|
||||
&[s-selected-parent] {
|
||||
// Display grid and allow edit marquee to display in nested layouts when editing
|
||||
// Display grid in nested layouts when editing
|
||||
> * > * > .l-layout {
|
||||
background: $editUIGridColorBg;
|
||||
box-shadow: inset $editUIGridColorFg 0 0 2px 1px;
|
||||
@@ -118,21 +95,10 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*********************** EDIT MARQUEE CONTROL */
|
||||
*[s-selected-parent] {
|
||||
> .l-layout {
|
||||
// When main shell layout is the parent
|
||||
@include displayMarquee(deeppink);
|
||||
}
|
||||
> * > * > * {
|
||||
// When a sub-layout is the parent
|
||||
@include displayMarquee(blue);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
<script>
|
||||
import uuid from 'uuid';
|
||||
|
||||
@@ -142,7 +108,6 @@
|
||||
import TextView from './TextView.vue'
|
||||
import LineView from './LineView.vue'
|
||||
import ImageView from './ImageView.vue'
|
||||
import EditMarquee from './EditMarquee.vue'
|
||||
|
||||
const ITEM_TYPE_VIEW_MAP = {
|
||||
'subobject-view': SubobjectView,
|
||||
@@ -158,10 +123,8 @@
|
||||
down: -1,
|
||||
bottom: Number.NEGATIVE_INFINITY
|
||||
};
|
||||
const DRAG_OBJECT_TRANSFER_PREFIX = 'openmct/domain-object/';
|
||||
|
||||
let components = ITEM_TYPE_VIEW_MAP;
|
||||
components['edit-marquee'] = EditMarquee;
|
||||
const DRAG_OBJECT_TRANSFER_PREFIX = 'openmct/domain-object/';
|
||||
|
||||
function getItemDefinition(itemType, ...options) {
|
||||
let itemView = ITEM_TYPE_VIEW_MAP[itemType];
|
||||
@@ -178,8 +141,7 @@
|
||||
let domainObject = JSON.parse(JSON.stringify(this.domainObject));
|
||||
return {
|
||||
internalDomainObject: domainObject,
|
||||
initSelectIndex: undefined,
|
||||
selection: []
|
||||
initSelectIndex: undefined
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -188,145 +150,82 @@
|
||||
},
|
||||
layoutItems() {
|
||||
return this.internalDomainObject.configuration.items;
|
||||
},
|
||||
selectedLayoutItems() {
|
||||
return this.layoutItems.filter(item => {
|
||||
return this.itemIsInCurrentSelection(item);
|
||||
});
|
||||
},
|
||||
showMarquee() {
|
||||
let selectionPath = this.selection[0];
|
||||
let singleSelectedLine = this.selection.length === 1 &&
|
||||
selectionPath[0].context.layoutItem && selectionPath[0].context.layoutItem.type === 'line-view';
|
||||
return selectionPath && selectionPath.length > 1 && !singleSelectedLine;
|
||||
}
|
||||
},
|
||||
inject: ['openmct', 'options'],
|
||||
inject: ['openmct'],
|
||||
props: ['domainObject'],
|
||||
components: components,
|
||||
components: ITEM_TYPE_VIEW_MAP,
|
||||
methods: {
|
||||
addElement(itemType, element) {
|
||||
this.addItem(itemType + '-view', element);
|
||||
},
|
||||
setSelection(selection) {
|
||||
this.selection = selection;
|
||||
if (selection.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.removeSelectionListener) {
|
||||
this.removeSelectionListener();
|
||||
}
|
||||
|
||||
let itemIndex = selection[0].context.index;
|
||||
|
||||
if (itemIndex !== undefined) {
|
||||
this.attachSelectionListener(itemIndex);
|
||||
}
|
||||
},
|
||||
itemIsInCurrentSelection(item) {
|
||||
return this.selection.some(selectionPath =>
|
||||
selectionPath[0].context.layoutItem && selectionPath[0].context.layoutItem.id === item.id);
|
||||
attachSelectionListener(index) {
|
||||
let path = `configuration.items[${index}].useGrid`;
|
||||
this.removeSelectionListener = this.openmct.objects.observe(this.internalDomainObject, path, function (value) {
|
||||
let item = this.layoutItems[index];
|
||||
|
||||
if (value) {
|
||||
item.x = Math.round(item.x / this.gridSize[0]);
|
||||
item.y = Math.round(item.y / this.gridSize[1]);
|
||||
item.width = Math.round(item.width / this.gridSize[0]);
|
||||
item.height = Math.round(item.height / this.gridSize[1]);
|
||||
|
||||
if (item.x2) {
|
||||
item.x2 = Math.round(item.x2 / this.gridSize[0]);
|
||||
}
|
||||
if (item.y2) {
|
||||
item.y2 = Math.round(item.y2 / this.gridSize[1]);
|
||||
}
|
||||
} else {
|
||||
item.x = this.gridSize[0] * item.x;
|
||||
item.y = this.gridSize[1] * item.y;
|
||||
item.width = this.gridSize[0] * item.width;
|
||||
item.height = this.gridSize[1] * item.height;
|
||||
|
||||
if (item.x2) {
|
||||
item.x2 = this.gridSize[0] * item.x2;
|
||||
}
|
||||
if (item.y2) {
|
||||
item.y2 = this.gridSize[1] * item.y2;
|
||||
}
|
||||
}
|
||||
item.useGrid = value;
|
||||
this.mutate(`configuration.items[${index}]`, item);
|
||||
}.bind(this));
|
||||
},
|
||||
bypassSelection($event) {
|
||||
if (this.dragInProgress) {
|
||||
if ($event) {
|
||||
$event.stopImmediatePropagation();
|
||||
}
|
||||
this.dragInProgress = false;
|
||||
return;
|
||||
}
|
||||
},
|
||||
endLineResize(item, updates) {
|
||||
endDrag(item, updates) {
|
||||
this.dragInProgress = true;
|
||||
setTimeout(function () {
|
||||
this.dragInProgress = false;
|
||||
}.bind(this), 0);
|
||||
|
||||
let index = this.layoutItems.indexOf(item);
|
||||
Object.assign(item, updates);
|
||||
this.mutate(`configuration.items[${index}]`, item);
|
||||
},
|
||||
endResize(scaleWidth, scaleHeight, marqueeStart, marqueeOffset) {
|
||||
this.dragInProgress = true;
|
||||
this.layoutItems.forEach(item => {
|
||||
if (this.itemIsInCurrentSelection(item)) {
|
||||
let itemXInMarqueeSpace = item.x - marqueeStart.x;
|
||||
let itemXInMarqueeSpaceAfterScale = Math.round(itemXInMarqueeSpace * scaleWidth);
|
||||
item.x = itemXInMarqueeSpaceAfterScale + marqueeOffset.x + marqueeStart.x;
|
||||
|
||||
let itemYInMarqueeSpace = item.y - marqueeStart.y;
|
||||
let itemYInMarqueeSpaceAfterScale = Math.round(itemYInMarqueeSpace * scaleHeight);
|
||||
item.y = itemYInMarqueeSpaceAfterScale + marqueeOffset.y + marqueeStart.y;
|
||||
|
||||
if (item.x2) {
|
||||
let itemX2InMarqueeSpace = item.x2 - marqueeStart.x;
|
||||
let itemX2InMarqueeSpaceAfterScale = Math.round(itemX2InMarqueeSpace * scaleWidth);
|
||||
item.x2 = itemX2InMarqueeSpaceAfterScale + marqueeOffset.x + marqueeStart.x;
|
||||
} else {
|
||||
item.width = Math.round(item.width * scaleWidth);
|
||||
}
|
||||
|
||||
if (item.y2) {
|
||||
let itemY2InMarqueeSpace = item.y2 - marqueeStart.y;
|
||||
let itemY2InMarqueeSpaceAfterScale = Math.round(itemY2InMarqueeSpace * scaleHeight);
|
||||
item.y2 = itemY2InMarqueeSpaceAfterScale + marqueeOffset.y + marqueeStart.y;
|
||||
} else {
|
||||
item.height = Math.round(item.height * scaleHeight);
|
||||
}
|
||||
}
|
||||
});
|
||||
this.mutate("configuration.items", this.layoutItems);
|
||||
},
|
||||
move(gridDelta) {
|
||||
this.dragInProgress = true;
|
||||
|
||||
if (!this.initialPositions) {
|
||||
this.initialPositions = {};
|
||||
_.cloneDeep(this.selectedLayoutItems).forEach(selectedItem => {
|
||||
if (selectedItem.type === 'line-view') {
|
||||
this.initialPositions[selectedItem.id] = [selectedItem.x, selectedItem.y, selectedItem.x2, selectedItem.y2];
|
||||
this.startingMinX2 = this.startingMinX2 !== undefined ? Math.min(this.startingMinX2, selectedItem.x2) : selectedItem.x2;
|
||||
this.startingMinY2 = this.startingMinY2 !== undefined ? Math.min(this.startingMinY2, selectedItem.y2) : selectedItem.y2;
|
||||
} else {
|
||||
this.initialPositions[selectedItem.id] = [selectedItem.x, selectedItem.y];
|
||||
}
|
||||
|
||||
this.startingMinX = this.startingMinX !== undefined ? Math.min(this.startingMinX, selectedItem.x) : selectedItem.x;
|
||||
this.startingMinY = this.startingMinY !== undefined ? Math.min(this.startingMinY, selectedItem.y) : selectedItem.y;
|
||||
});
|
||||
}
|
||||
|
||||
let layoutItems = this.layoutItems.map(item => {
|
||||
if (this.initialPositions[item.id]) {
|
||||
this.updateItemPosition(item, gridDelta);
|
||||
}
|
||||
return item;
|
||||
});
|
||||
},
|
||||
updateItemPosition(item, gridDelta) {
|
||||
let startingPosition = this.initialPositions[item.id];
|
||||
let [startingX, startingY, startingX2, startingY2] = startingPosition;
|
||||
|
||||
if (this.startingMinX + gridDelta[0] >= 0) {
|
||||
if (item.x2 !== undefined) {
|
||||
if (this.startingMinX2 + gridDelta[0] >= 0) {
|
||||
item.x = startingX + gridDelta[0];
|
||||
}
|
||||
} else {
|
||||
item.x = startingX + gridDelta[0];
|
||||
}
|
||||
}
|
||||
|
||||
if (this.startingMinY + gridDelta[1] >= 0) {
|
||||
if (item.y2 !== undefined) {
|
||||
if (this.startingMinY2 + gridDelta[1] >= 0) {
|
||||
item.y = startingY + gridDelta[1];
|
||||
}
|
||||
} else {
|
||||
item.y = startingY + gridDelta[1];
|
||||
}
|
||||
}
|
||||
|
||||
if (item.x2 !== undefined && this.startingMinX2 + gridDelta[0] >= 0 && this.startingMinX + gridDelta[0] >= 0) {
|
||||
item.x2 = startingX2 + gridDelta[0];
|
||||
}
|
||||
|
||||
if (item.y2 !== undefined && this.startingMinY2 + gridDelta[1] >= 0 && this.startingMinY + gridDelta[1] >= 0) {
|
||||
item.y2 = startingY2 + gridDelta[1];
|
||||
}
|
||||
},
|
||||
endMove() {
|
||||
this.mutate('configuration.items', this.layoutItems);
|
||||
this.initialPositions = undefined;
|
||||
this.startingMinX = undefined;
|
||||
this.startingMinY = undefined;
|
||||
this.startingMinX2 = undefined;
|
||||
this.startingMinY2 = undefined;
|
||||
},
|
||||
mutate(path, value) {
|
||||
this.openmct.objects.mutate(this.internalDomainObject, path, value);
|
||||
},
|
||||
@@ -384,8 +283,9 @@
|
||||
}
|
||||
},
|
||||
isTelemetry(domainObject) {
|
||||
if (this.openmct.telemetry.isTelemetryObject(domainObject) &&
|
||||
!this.options.showAsView.includes(domainObject.type)) {
|
||||
if (this.openmct.telemetry.isTelemetryObject(domainObject)
|
||||
&& domainObject.type !== 'summary-widget'
|
||||
&& domainObject.type !== 'example.imagery') {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@@ -414,15 +314,11 @@
|
||||
this.objectViewMap[keyString] = true;
|
||||
}
|
||||
},
|
||||
removeItem(selectedItems) {
|
||||
let indices = [];
|
||||
removeItem(item, index) {
|
||||
this.initSelectIndex = -1;
|
||||
selectedItems.forEach(selectedItem => {
|
||||
indices.push(selectedItem[0].context.index);
|
||||
this.untrackItem(selectedItem[0].context.layoutItem);
|
||||
});
|
||||
_.pullAt(this.layoutItems, indices);
|
||||
this.layoutItems.splice(index, 1);
|
||||
this.mutate("configuration.items", this.layoutItems);
|
||||
this.untrackItem(item);
|
||||
this.$el.click();
|
||||
},
|
||||
untrackItem(item) {
|
||||
@@ -488,74 +384,20 @@
|
||||
this.mutate("configuration.items", layoutItems);
|
||||
this.$el.click();
|
||||
},
|
||||
orderItem(position, selectedItems) {
|
||||
orderItem(position, index) {
|
||||
let delta = ORDERS[position];
|
||||
let indices = [];
|
||||
let newIndex = -1;
|
||||
let items = [];
|
||||
let newIndex = Math.max(Math.min(index + delta, this.layoutItems.length - 1), 0);
|
||||
let item = this.layoutItems[index];
|
||||
|
||||
Object.assign(items, this.layoutItems);
|
||||
this.selectedLayoutItems.forEach(selectedItem => {
|
||||
indices.push(this.layoutItems.indexOf(selectedItem));
|
||||
});
|
||||
indices.sort((a, b) => a - b);
|
||||
if (newIndex !== index) {
|
||||
this.layoutItems.splice(index, 1);
|
||||
this.layoutItems.splice(newIndex, 0, item);
|
||||
this.mutate('configuration.items', this.layoutItems);
|
||||
|
||||
if (position === 'top' || position === 'up') {
|
||||
indices.reverse();
|
||||
}
|
||||
|
||||
if (position === 'top' || position === 'bottom') {
|
||||
this.moveToTopOrBottom(position, indices, items, delta);
|
||||
} else {
|
||||
this.moveUpOrDown(position, indices, items, delta);
|
||||
}
|
||||
|
||||
this.mutate('configuration.items', this.layoutItems);
|
||||
},
|
||||
moveUpOrDown(position, indices, items, delta) {
|
||||
let previousItemIndex = -1;
|
||||
let newIndex = -1;
|
||||
|
||||
indices.forEach((itemIndex, index) => {
|
||||
let isAdjacentItemSelected = position === 'up' ?
|
||||
itemIndex + 1 === previousItemIndex :
|
||||
itemIndex - 1 === previousItemIndex;
|
||||
|
||||
if (index > 0 && isAdjacentItemSelected) {
|
||||
if (position === 'up') {
|
||||
newIndex -= 1;
|
||||
} else {
|
||||
newIndex += 1;
|
||||
}
|
||||
} else {
|
||||
newIndex = Math.max(Math.min(itemIndex + delta, this.layoutItems.length - 1), 0);
|
||||
if (this.removeSelectionListener) {
|
||||
this.removeSelectionListener();
|
||||
this.attachSelectionListener(newIndex);
|
||||
}
|
||||
|
||||
previousItemIndex = itemIndex;
|
||||
this.updateItemOrder(newIndex, itemIndex, items);
|
||||
});
|
||||
},
|
||||
moveToTopOrBottom(position, indices, items, delta) {
|
||||
let newIndex = -1;
|
||||
|
||||
indices.forEach((itemIndex, index) => {
|
||||
if (index === 0) {
|
||||
newIndex = Math.max(Math.min(itemIndex + delta, this.layoutItems.length - 1), 0);
|
||||
} else {
|
||||
if (position === 'top') {
|
||||
newIndex -= 1;
|
||||
} else {
|
||||
newIndex += 1;
|
||||
}
|
||||
}
|
||||
|
||||
this.updateItemOrder(newIndex, itemIndex, items);
|
||||
});
|
||||
},
|
||||
updateItemOrder(newIndex, itemIndex, items) {
|
||||
if (newIndex !== itemIndex) {
|
||||
this.layoutItems.splice(itemIndex, 1);
|
||||
this.layoutItems.splice(newIndex, 0, items[itemIndex]);
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -571,10 +413,14 @@
|
||||
this.composition.load();
|
||||
},
|
||||
destroyed: function () {
|
||||
this.openmct.selection.off('change', this.setSelection);
|
||||
this.openmct.off('change', this.setSelection);
|
||||
this.composition.off('add', this.addChild);
|
||||
this.composition.off('remove', this.removeChild);
|
||||
this.unlisten();
|
||||
|
||||
if (this.removeSelectionListener) {
|
||||
this.removeSelectionListener();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,233 +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.
|
||||
*****************************************************************************/
|
||||
|
||||
<template>
|
||||
<!-- Resize handles -->
|
||||
<div class="c-frame-edit" :style="style">
|
||||
<div class="c-frame-edit__handle c-frame-edit__handle--nw"
|
||||
@mousedown="startResize([1,1], [-1,-1], $event)"></div>
|
||||
<div class="c-frame-edit__handle c-frame-edit__handle--ne"
|
||||
@mousedown="startResize([0,1], [1,-1], $event)"></div>
|
||||
<div class="c-frame-edit__handle c-frame-edit__handle--sw"
|
||||
@mousedown="startResize([1,0], [-1,1], $event)"></div>
|
||||
<div class="c-frame-edit__handle c-frame-edit__handle--se"
|
||||
@mousedown="startResize([0,0], [1,1], $event)"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@import "~styles/sass-base";
|
||||
|
||||
.c-frame-edit {
|
||||
// In Layouts, this is the editing rect and handles
|
||||
display: none; // Set to display: block in DisplayLayout.vue
|
||||
pointer-events: none;
|
||||
@include abs();
|
||||
border: $editMarqueeBorder;
|
||||
|
||||
&__handle {
|
||||
$d: 6px;
|
||||
$o: floor($d * -0.5);
|
||||
background: $editFrameColorHandleFg;
|
||||
box-shadow: $editFrameColorHandleBg 0 0 0 2px;
|
||||
pointer-events: all;
|
||||
position: absolute;
|
||||
width: $d; height: $d;
|
||||
top: auto; right: auto; bottom: auto; left: auto;
|
||||
|
||||
&:before {
|
||||
// Extended hit area
|
||||
@include abs(-10px);
|
||||
content: '';
|
||||
display: block;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: $editUIColor;
|
||||
}
|
||||
|
||||
&--nwse {
|
||||
cursor: nwse-resize;
|
||||
}
|
||||
|
||||
&--nw {
|
||||
cursor: nw-resize;
|
||||
left: $o; top: $o;
|
||||
}
|
||||
|
||||
&--ne {
|
||||
cursor: ne-resize;
|
||||
right: $o; top: $o;
|
||||
}
|
||||
|
||||
&--se {
|
||||
cursor: se-resize;
|
||||
right: $o; bottom: $o;
|
||||
}
|
||||
|
||||
&--sw {
|
||||
cursor: sw-resize;
|
||||
left: $o; bottom: $o;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
<script>
|
||||
import LayoutDrag from './../LayoutDrag'
|
||||
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
props: {
|
||||
selectedLayoutItems: Array,
|
||||
gridSize: Array
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dragPosition: undefined
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
style() {
|
||||
let x = Number.POSITIVE_INFINITY;
|
||||
let y = Number.POSITIVE_INFINITY;
|
||||
let width = Number.NEGATIVE_INFINITY;
|
||||
let height = Number.NEGATIVE_INFINITY;
|
||||
|
||||
this.selectedLayoutItems.forEach(item => {
|
||||
if (item.x2 !== undefined) {
|
||||
let lineWidth = Math.abs(item.x - item.x2);
|
||||
let lineMinX = Math.min(item.x, item.x2);
|
||||
x = Math.min(lineMinX, x);
|
||||
width = Math.max(lineWidth + lineMinX, width);
|
||||
} else {
|
||||
x = Math.min(item.x, x);
|
||||
width = Math.max(item.width + item.x, width);
|
||||
}
|
||||
|
||||
if (item.y2 !== undefined) {
|
||||
let lineHeight = Math.abs(item.y - item.y2);
|
||||
let lineMinY = Math.min(item.y, item.y2);
|
||||
y = Math.min(lineMinY, y);
|
||||
height = Math.max(lineHeight + lineMinY, height);
|
||||
} else {
|
||||
y = Math.min(item.y, y);
|
||||
height = Math.max(item.height + item.y, height);
|
||||
}
|
||||
});
|
||||
|
||||
if (this.dragPosition) {
|
||||
[x, y] = this.dragPosition.position;
|
||||
[width, height] = this.dragPosition.dimensions;
|
||||
} else {
|
||||
width = width - x;
|
||||
height = height - y;
|
||||
}
|
||||
|
||||
this.marqueePosition = {
|
||||
x: x,
|
||||
y: y,
|
||||
width: width,
|
||||
height: height
|
||||
}
|
||||
return this.getMarqueeStyle(x, y, width, height);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getMarqueeStyle(x, y, width, height) {
|
||||
return {
|
||||
left: (this.gridSize[0] * x) + 'px',
|
||||
top: (this.gridSize[1] * y) + 'px',
|
||||
width: (this.gridSize[0] * width) + 'px',
|
||||
height: (this.gridSize[1] * height) + 'px'
|
||||
};
|
||||
},
|
||||
updatePosition(event) {
|
||||
let currentPosition = [event.pageX, event.pageY];
|
||||
this.initialPosition = this.initialPosition || currentPosition;
|
||||
this.delta = currentPosition.map(function (value, index) {
|
||||
return value - this.initialPosition[index];
|
||||
}.bind(this));
|
||||
},
|
||||
startResize(posFactor, dimFactor, event) {
|
||||
document.body.addEventListener('mousemove', this.continueResize);
|
||||
document.body.addEventListener('mouseup', this.endResize);
|
||||
this.marqueeStartPosition = {
|
||||
position: [this.marqueePosition.x, this.marqueePosition.y],
|
||||
dimensions: [this.marqueePosition.width, this.marqueePosition.height]
|
||||
};
|
||||
this.updatePosition(event);
|
||||
this.activeDrag = new LayoutDrag(this.marqueeStartPosition, posFactor, dimFactor, this.gridSize);
|
||||
event.preventDefault();
|
||||
},
|
||||
continueResize(event) {
|
||||
event.preventDefault();
|
||||
this.updatePosition(event);
|
||||
this.dragPosition = this.activeDrag.getAdjustedPositionAndDimensions(this.delta);
|
||||
},
|
||||
endResize(event) {
|
||||
document.body.removeEventListener('mousemove', this.continueResize);
|
||||
document.body.removeEventListener('mouseup', this.endResize);
|
||||
this.continueResize(event);
|
||||
|
||||
let marqueeStartWidth = this.marqueeStartPosition.dimensions[0];
|
||||
let marqueeStartHeight = this.marqueeStartPosition.dimensions[1];
|
||||
let marqueeStartX = this.marqueeStartPosition.position[0];
|
||||
let marqueeStartY = this.marqueeStartPosition.position[1];
|
||||
|
||||
let marqueeEndX = this.dragPosition.position[0];
|
||||
let marqueeEndY = this.dragPosition.position[1];
|
||||
let marqueeEndWidth = this.dragPosition.dimensions[0];
|
||||
let marqueeEndHeight = this.dragPosition.dimensions[1];
|
||||
|
||||
let scaleWidth = marqueeEndWidth / marqueeStartWidth;
|
||||
let scaleHeight = marqueeEndHeight / marqueeStartHeight;
|
||||
|
||||
let marqueeStart = {
|
||||
x: marqueeStartX,
|
||||
y: marqueeStartY,
|
||||
height: marqueeStartWidth,
|
||||
width: marqueeStartHeight
|
||||
};
|
||||
let marqueeEnd = {
|
||||
x: marqueeEndX,
|
||||
y: marqueeEndY,
|
||||
width: marqueeEndWidth,
|
||||
height: marqueeEndHeight
|
||||
};
|
||||
let marqueeOffset = {
|
||||
x: marqueeEnd.x - marqueeStart.x,
|
||||
y: marqueeEnd.y - marqueeStart.y
|
||||
};
|
||||
|
||||
this.$emit('endResize', scaleWidth, scaleHeight, marqueeStart, marqueeOffset);
|
||||
this.dragPosition = undefined;
|
||||
this.initialPosition = undefined;
|
||||
this.marqueeStartPosition = undefined;
|
||||
this.delta = undefined;
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -23,8 +23,7 @@
|
||||
<template>
|
||||
<layout-frame :item="item"
|
||||
:grid-size="gridSize"
|
||||
@move="(gridDelta) => $emit('move', gridDelta)"
|
||||
@endMove="() => $emit('endMove')">
|
||||
@endDrag="(item, updates) => $emit('endDrag', item, updates)">
|
||||
<div class="c-image-view"
|
||||
:style="style">
|
||||
</div>
|
||||
@@ -57,7 +56,8 @@
|
||||
y: 1,
|
||||
width: 10,
|
||||
height: 5,
|
||||
url: element.url
|
||||
url: element.url,
|
||||
useGrid: true
|
||||
};
|
||||
},
|
||||
inject: ['openmct'],
|
||||
|
||||
@@ -24,14 +24,25 @@
|
||||
<div class="l-layout__frame c-frame"
|
||||
:class="{
|
||||
'no-frame': !item.hasFrame,
|
||||
'u-inspectable': inspectable
|
||||
'u-inspectable': inspectable,
|
||||
'is-resizing': isResizing
|
||||
}"
|
||||
:style="style">
|
||||
|
||||
<slot></slot>
|
||||
|
||||
<div class="c-frame-edit__move"
|
||||
@mousedown="startMove([1,1], [0,0], $event)">
|
||||
<!-- Drag handles -->
|
||||
<div class="c-frame-edit">
|
||||
<div class="c-frame-edit__move"
|
||||
@mousedown="startDrag([1,1], [0,0], $event, 'move')"></div>
|
||||
<div class="c-frame-edit__handle c-frame-edit__handle--nw"
|
||||
@mousedown="startDrag([1,1], [-1,-1], $event, 'resize')"></div>
|
||||
<div class="c-frame-edit__handle c-frame-edit__handle--ne"
|
||||
@mousedown="startDrag([0,1], [1,-1], $event, 'resize')"></div>
|
||||
<div class="c-frame-edit__handle c-frame-edit__handle--sw"
|
||||
@mousedown="startDrag([1,0], [-1,1], $event, 'resize')"></div>
|
||||
<div class="c-frame-edit__handle c-frame-edit__handle--se"
|
||||
@mousedown="startDrag([0,0], [1,1], $event, 'resize')"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -39,7 +50,7 @@
|
||||
<style lang="scss">
|
||||
@import "~styles/sass-base";
|
||||
|
||||
/******************* FRAME */
|
||||
/******************************* FRAME */
|
||||
.c-frame {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -48,15 +59,124 @@
|
||||
> *:first-child {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
&:not(.no-frame) {
|
||||
background: $colorBodyBg;
|
||||
border: $browseFrameBorder;
|
||||
padding: $interiorMargin;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.c-frame-edit__move {
|
||||
.c-frame-edit {
|
||||
// In Layouts, this is the editing rect and handles
|
||||
// In Fixed Position, this is a wrapper element
|
||||
@include abs();
|
||||
display: none;
|
||||
|
||||
&__move {
|
||||
@include abs();
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
&__handle {
|
||||
$d: 6px;
|
||||
$o: floor($d * -0.5);
|
||||
background: $editFrameColorHandleFg;
|
||||
box-shadow: $editFrameColorHandleBg 0 0 0 2px;
|
||||
display: none; // Set to block via s-selected selector
|
||||
position: absolute;
|
||||
width: $d; height: $d;
|
||||
top: auto; right: auto; bottom: auto; left: auto;
|
||||
|
||||
&:before {
|
||||
// Extended hit area
|
||||
@include abs(-10px);
|
||||
content: '';
|
||||
display: block;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: $editUIColor;
|
||||
}
|
||||
|
||||
&--nwse {
|
||||
cursor: nwse-resize;
|
||||
}
|
||||
|
||||
&--nw {
|
||||
cursor: nw-resize;
|
||||
left: $o; top: $o;
|
||||
}
|
||||
|
||||
&--ne {
|
||||
cursor: ne-resize;
|
||||
right: $o; top: $o;
|
||||
}
|
||||
|
||||
&--se {
|
||||
cursor: se-resize;
|
||||
right: $o; bottom: $o;
|
||||
}
|
||||
|
||||
&--sw {
|
||||
cursor: sw-resize;
|
||||
left: $o; bottom: $o;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.c-so-view.has-complex-content + .c-frame-edit {
|
||||
// Target frames that hold domain objects that include header elements, as opposed to drawing and alpha objects
|
||||
// Make the __move element a more affordable drag UI element
|
||||
.c-frame-edit__move {
|
||||
@include userSelectNone();
|
||||
background: $editFrameMovebarColorBg;
|
||||
box-shadow: rgba(black, 0.2) 0 1px;
|
||||
bottom: auto;
|
||||
height: 0; // Height is set on hover on s-selected.c-frame
|
||||
opacity: 0.8;
|
||||
max-height: 100%;
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
|
||||
&:before {
|
||||
// Grippy
|
||||
$h: 4px;
|
||||
$tbOffset: ($editFrameMovebarH - $h) / 2;
|
||||
$lrOffset: 25%;
|
||||
@include grippy($editFrameMovebarColorFg);
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: $tbOffset; right: $lrOffset; bottom: $tbOffset; left: $lrOffset;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: $editFrameHovMovebarColorBg;
|
||||
&:before { @include grippy($editFrameHovMovebarColorFg); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.is-editing {
|
||||
/******************* STYLES FOR C-FRAME WHILE EDITING */
|
||||
.c-frame {
|
||||
$moveBarOutDelay: 500ms;
|
||||
&.no-frame {
|
||||
border: $editFrameBorder; // Base border style for a frame element while editing.
|
||||
}
|
||||
|
||||
&-edit {
|
||||
display: contents;
|
||||
}
|
||||
|
||||
&-edit__move,
|
||||
.c-so-view {
|
||||
transition: $transOut;
|
||||
transition-delay: $moveBarOutDelay;
|
||||
}
|
||||
|
||||
&:not([s-selected]) {
|
||||
&:hover {
|
||||
border: $editFrameBorderHov;
|
||||
@@ -68,110 +188,37 @@
|
||||
border: $editFrameSelectedBorder;
|
||||
box-shadow: $editFrameSelectedShdw;
|
||||
|
||||
.c-frame-edit__move {
|
||||
cursor: move;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/******************* DEFAULT STYLES FOR -EDIT__MOVE */
|
||||
// All object types
|
||||
.c-frame-edit__move {
|
||||
@include abs();
|
||||
display: block;
|
||||
}
|
||||
|
||||
// Has-complex-content objects
|
||||
.c-so-view.has-complex-content {
|
||||
transition: $transOut;
|
||||
transition-delay: $moveBarOutDelay;
|
||||
|
||||
> .c-so-view__local-controls {
|
||||
transition: transform 250ms ease-in-out;
|
||||
transition-delay: $moveBarOutDelay;
|
||||
}
|
||||
|
||||
+ .c-frame-edit__move {
|
||||
display: none;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.l-layout {
|
||||
/******************* 0 - 1 ITEM SELECTED */
|
||||
&:not(.is-multi-selected) {
|
||||
> .l-layout__frame[s-selected] {
|
||||
> .c-so-view.has-complex-content {
|
||||
> .c-so-view__local-controls {
|
||||
transition: transform $transOutTime ease-in-out;
|
||||
transition-delay: $moveBarOutDelay;
|
||||
}
|
||||
|
||||
+ .c-frame-edit__move {
|
||||
transition: $transOut;
|
||||
transition-delay: $moveBarOutDelay;
|
||||
@include userSelectNone();
|
||||
background: $editFrameMovebarColorBg;
|
||||
box-shadow: rgba(black, 0.2) 0 1px;
|
||||
bottom: auto;
|
||||
display: block;
|
||||
height: 0; // Height is set on hover below
|
||||
opacity: 0.8;
|
||||
max-height: 100%;
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
|
||||
&:before {
|
||||
// Grippy
|
||||
$h: 4px;
|
||||
$tbOffset: ($editFrameMovebarH - $h) / 2;
|
||||
$lrOffset: 25%;
|
||||
@include grippy($editFrameMovebarColorFg);
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: $tbOffset;
|
||||
right: $lrOffset;
|
||||
bottom: $tbOffset;
|
||||
left: $lrOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
> .c-so-view.has-complex-content {
|
||||
transition: $transIn;
|
||||
transition-delay: 0s;
|
||||
padding-top: $editFrameMovebarH + $interiorMarginSm;
|
||||
|
||||
> .c-so-view__local-controls {
|
||||
transform: translateY($editFrameMovebarH);
|
||||
transition: transform $transInTime ease-in-out;
|
||||
transition-delay: 0s;
|
||||
}
|
||||
|
||||
+ .c-frame-edit__move {
|
||||
transition: $transIn;
|
||||
transition-delay: 0s;
|
||||
height: $editFrameMovebarH;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/******************* > 1 ITEMS SELECTED */
|
||||
&.is-multi-selected {
|
||||
.l-layout__frame[s-selected] {
|
||||
> .c-so-view.has-complex-content + .c-frame-edit__move {
|
||||
> .c-frame-edit {
|
||||
[class*='__handle'] {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.l-layout__frame:not(.is-resizing) {
|
||||
// Show and animate the __move bar for sub-object views with complex content
|
||||
&:hover > .c-so-view.has-complex-content {
|
||||
// Move content down so the __move bar doesn't cover it.
|
||||
padding-top: $editFrameMovebarH;
|
||||
transition: $transIn;
|
||||
|
||||
&.c-so-view--no-frame {
|
||||
// Move content down with a bit more space
|
||||
padding-top: $editFrameMovebarH + $interiorMarginSm;
|
||||
}
|
||||
|
||||
// Show the move bar
|
||||
+ .c-frame-edit .c-frame-edit__move {
|
||||
height: $editFrameMovebarH;
|
||||
transition: $transIn;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
<script>
|
||||
import LayoutDrag from './../LayoutDrag'
|
||||
|
||||
@@ -181,9 +228,21 @@
|
||||
item: Object,
|
||||
gridSize: Array
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dragPosition: undefined,
|
||||
isResizing: undefined
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
style() {
|
||||
let {x, y, width, height} = this.item;
|
||||
|
||||
if (this.dragPosition) {
|
||||
[x, y] = this.dragPosition.position;
|
||||
[width, height] = this.dragPosition.dimensions;
|
||||
}
|
||||
|
||||
return {
|
||||
left: (this.gridSize[0] * x) + 'px',
|
||||
top: (this.gridSize[1] * y) + 'px',
|
||||
@@ -205,40 +264,36 @@
|
||||
return value - this.initialPosition[index];
|
||||
}.bind(this));
|
||||
},
|
||||
startMove(posFactor, dimFactor, event) {
|
||||
document.body.addEventListener('mousemove', this.continueMove);
|
||||
document.body.addEventListener('mouseup', this.endMove);
|
||||
startDrag(posFactor, dimFactor, event, type) {
|
||||
document.body.addEventListener('mousemove', this.continueDrag);
|
||||
document.body.addEventListener('mouseup', this.endDrag);
|
||||
|
||||
this.dragPosition = {
|
||||
position: [this.item.x, this.item.y]
|
||||
position: [this.item.x, this.item.y],
|
||||
dimensions: [this.item.width, this.item.height]
|
||||
};
|
||||
this.updatePosition(event);
|
||||
this.activeDrag = new LayoutDrag(this.dragPosition, posFactor, dimFactor, this.gridSize);
|
||||
this.isResizing = type === 'resize';
|
||||
event.preventDefault();
|
||||
},
|
||||
continueMove(event) {
|
||||
continueDrag(event) {
|
||||
event.preventDefault();
|
||||
this.updatePosition(event);
|
||||
let newPosition = this.activeDrag.getAdjustedPosition(this.delta);
|
||||
|
||||
if (!_.isEqual(newPosition, this.dragPosition)) {
|
||||
this.dragPosition = newPosition;
|
||||
this.$emit('move', this.toGridDelta(this.delta));
|
||||
}
|
||||
this.dragPosition = this.activeDrag.getAdjustedPosition(this.delta);
|
||||
},
|
||||
endMove(event) {
|
||||
document.body.removeEventListener('mousemove', this.continueMove);
|
||||
document.body.removeEventListener('mouseup', this.endMove);
|
||||
this.continueMove(event);
|
||||
this.$emit('endMove');
|
||||
endDrag(event) {
|
||||
document.body.removeEventListener('mousemove', this.continueDrag);
|
||||
document.body.removeEventListener('mouseup', this.endDrag);
|
||||
this.continueDrag(event);
|
||||
let [x, y] = this.dragPosition.position;
|
||||
let [width, height] = this.dragPosition.dimensions;
|
||||
this.$emit('endDrag', this.item, {x, y, width, height});
|
||||
this.dragPosition = undefined;
|
||||
this.initialPosition = undefined;
|
||||
this.delta = undefined;
|
||||
this.isResizing = undefined;
|
||||
event.preventDefault();
|
||||
},
|
||||
toGridDelta(pixelDelta) {
|
||||
return pixelDelta.map((v, i) => {
|
||||
return Math.round(v / this.gridSize[i]);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,9 +30,9 @@
|
||||
</line>
|
||||
</svg>
|
||||
|
||||
<div class="c-frame-edit__move"
|
||||
@mousedown="startDrag($event)"></div>
|
||||
<div class="c-frame-edit" v-if="showFrameEdit">
|
||||
<div class="c-frame-edit">
|
||||
<div class="c-frame-edit__move"
|
||||
@mousedown="startDrag($event)"></div>
|
||||
<div class="c-frame-edit__handle"
|
||||
:class="startHandleClass"
|
||||
@mousedown="startDrag($event, 'start')"></div>
|
||||
@@ -66,7 +66,8 @@
|
||||
y: 10,
|
||||
x2: 10,
|
||||
y2: 5,
|
||||
stroke: '#717171'
|
||||
stroke: '#717171',
|
||||
useGrid: true
|
||||
};
|
||||
},
|
||||
inject: ['openmct'],
|
||||
@@ -75,31 +76,24 @@
|
||||
gridSize: Array,
|
||||
initSelect: Boolean,
|
||||
index: Number,
|
||||
multiSelect: Boolean
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dragPosition: undefined,
|
||||
dragging: undefined,
|
||||
selection: []
|
||||
dragPosition: undefined
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
showFrameEdit() {
|
||||
let layoutItem = this.selection.length > 0 && this.selection[0][0].context.layoutItem;
|
||||
return !this.multiSelect && layoutItem && layoutItem.id === this.item.id;
|
||||
},
|
||||
position() {
|
||||
let {x, y, x2, y2} = this.item;
|
||||
if (this.dragging && this.dragPosition) {
|
||||
if (this.dragPosition) {
|
||||
({x, y, x2, y2} = this.dragPosition);
|
||||
}
|
||||
return {x, y, x2, y2};
|
||||
},
|
||||
style() {
|
||||
let {x, y, x2, y2} = this.position;
|
||||
let width = Math.max(this.gridSize[0] * Math.abs(x - x2), 1);
|
||||
let height = Math.max(this.gridSize[1] * Math.abs(y - y2), 1);
|
||||
let width = this.gridSize[0] * Math.abs(x - x2);
|
||||
let height = this.gridSize[1] * Math.abs(y - y2);
|
||||
let left = this.gridSize[0] * Math.min(x, x2);
|
||||
let top = this.gridSize[1] * Math.min(y, y2);
|
||||
return {
|
||||
@@ -181,27 +175,13 @@
|
||||
event.preventDefault();
|
||||
let pxDeltaX = this.startPosition[0] - event.pageX;
|
||||
let pxDeltaY = this.startPosition[1] - event.pageY;
|
||||
let newPosition = this.calculateDragPosition(pxDeltaX, pxDeltaY);
|
||||
|
||||
if (!this.dragging) {
|
||||
if (!_.isEqual(newPosition, this.dragPosition)) {
|
||||
let gridDelta = [event.pageX - this.startPosition[0], event.pageY - this.startPosition[1]];
|
||||
this.dragPosition = newPosition;
|
||||
this.$emit('move', this.toGridDelta(gridDelta));
|
||||
}
|
||||
} else {
|
||||
this.dragPosition = newPosition;
|
||||
}
|
||||
this.dragPosition = this.calculateDragPosition(pxDeltaX, pxDeltaY);
|
||||
},
|
||||
endDrag(event) {
|
||||
document.body.removeEventListener('mousemove', this.continueDrag);
|
||||
document.body.removeEventListener('mouseup', this.endDrag);
|
||||
let {x, y, x2, y2} = this.dragPosition;
|
||||
if (!this.dragging) {
|
||||
this.$emit('endMove');
|
||||
} else {
|
||||
this.$emit('endLineResize', this.item, {x, y, x2, y2});
|
||||
}
|
||||
this.$emit('endDrag', this.item, {x, y, x2, y2});
|
||||
this.dragPosition = undefined;
|
||||
this.dragging = undefined;
|
||||
event.preventDefault();
|
||||
@@ -211,7 +191,6 @@
|
||||
let gridDeltaY = Math.round(pxDeltaY / this.gridSize[0]); // TODO: should this be gridSize[1]?
|
||||
let {x, y, x2, y2} = this.item;
|
||||
let dragPosition = {x, y, x2, y2};
|
||||
|
||||
if (this.dragging === 'start') {
|
||||
dragPosition.x -= gridDeltaX;
|
||||
dragPosition.y -= gridDeltaY;
|
||||
@@ -226,14 +205,6 @@
|
||||
dragPosition.y2 -= gridDeltaY;
|
||||
}
|
||||
return dragPosition;
|
||||
},
|
||||
setSelection(selection) {
|
||||
this.selection = selection;
|
||||
},
|
||||
toGridDelta(pixelDelta) {
|
||||
return pixelDelta.map((v, i) => {
|
||||
return Math.round(v / this.gridSize[i]);
|
||||
});
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -246,7 +217,6 @@
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.openmct.selection.on('change', this.setSelection);
|
||||
this.context = {
|
||||
layoutItem: this.item,
|
||||
index: this.index
|
||||
@@ -258,7 +228,6 @@
|
||||
if (this.removeSelectable) {
|
||||
this.removeSelectable();
|
||||
}
|
||||
this.openmct.selection.off('change', this.setSelection);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -22,9 +22,7 @@
|
||||
<template>
|
||||
<layout-frame :item="item"
|
||||
:grid-size="gridSize"
|
||||
:title="domainObject && domainObject.name"
|
||||
@move="(gridDelta) => $emit('move', gridDelta)"
|
||||
@endMove="() => $emit('endMove')">
|
||||
@endDrag="(item, updates) => $emit('endDrag', item, updates)">
|
||||
<object-frame v-if="domainObject"
|
||||
:domain-object="domainObject"
|
||||
:object-path="objectPath"
|
||||
@@ -68,7 +66,8 @@
|
||||
x: position[0],
|
||||
y: position[1],
|
||||
identifier: domainObject.identifier,
|
||||
hasFrame: hasFrameByDefault(domainObject.type)
|
||||
hasFrame: hasFrameByDefault(domainObject.type),
|
||||
useGrid: true
|
||||
};
|
||||
},
|
||||
inject: ['openmct'],
|
||||
|
||||
@@ -23,8 +23,7 @@
|
||||
<template>
|
||||
<layout-frame :item="item"
|
||||
:grid-size="gridSize"
|
||||
@move="(gridDelta) => $emit('move', gridDelta)"
|
||||
@endMove="() => $emit('endMove')">
|
||||
@endDrag="(item, updates) => $emit('endDrag', item, updates)">
|
||||
<div class="c-telemetry-view"
|
||||
:style="styleObject"
|
||||
v-if="domainObject">
|
||||
@@ -97,9 +96,10 @@
|
||||
displayMode: 'all',
|
||||
value: metadata.getDefaultDisplayValue(),
|
||||
stroke: "transparent",
|
||||
fill: "transparent",
|
||||
fill: "",
|
||||
color: "",
|
||||
size: "13px"
|
||||
size: "13px",
|
||||
useGrid: true
|
||||
};
|
||||
},
|
||||
inject: ['openmct'],
|
||||
@@ -176,8 +176,7 @@
|
||||
let options = {
|
||||
start: bounds.start,
|
||||
end: bounds.end,
|
||||
size: 1,
|
||||
strategy: 'latest'
|
||||
size: 1
|
||||
};
|
||||
this.openmct.telemetry.request(this.domainObject, options)
|
||||
.then(data => {
|
||||
|
||||
@@ -23,8 +23,7 @@
|
||||
<template>
|
||||
<layout-frame :item="item"
|
||||
:grid-size="gridSize"
|
||||
@move="(gridDelta) => $emit('move', gridDelta)"
|
||||
@endMove="() => $emit('endMove')">
|
||||
@endDrag="(item, updates) => $emit('endDrag', item, updates)">
|
||||
<div class="c-text-view"
|
||||
:style="style">
|
||||
{{ item.text }}
|
||||
@@ -60,7 +59,8 @@
|
||||
y: 1,
|
||||
width: 10,
|
||||
height: 5,
|
||||
text: element.text
|
||||
text: element.text,
|
||||
useGrid: true
|
||||
};
|
||||
},
|
||||
inject: ['openmct'],
|
||||
|
||||
@@ -25,7 +25,8 @@ 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 DisplayLayoutPlugin(options) {
|
||||
|
||||
export default function () {
|
||||
return function (openmct) {
|
||||
openmct.objectViews.addProvider({
|
||||
key: 'layout.view',
|
||||
@@ -46,8 +47,7 @@ export default function DisplayLayoutPlugin(options) {
|
||||
template: '<layout ref="displayLayout" :domain-object="domainObject"></layout>',
|
||||
provide: {
|
||||
openmct,
|
||||
objectUtils,
|
||||
options
|
||||
objectUtils
|
||||
},
|
||||
el: container,
|
||||
data () {
|
||||
@@ -60,7 +60,6 @@ export default function DisplayLayoutPlugin(options) {
|
||||
getSelectionContext() {
|
||||
return {
|
||||
item: domainObject,
|
||||
supportsMultiSelect: true,
|
||||
addElement: component && component.$refs.displayLayout.addElement,
|
||||
removeItem: component && component.$refs.displayLayout.removeItem,
|
||||
orderItem: component && component.$refs.displayLayout.orderItem
|
||||
@@ -84,6 +83,5 @@ export default function DisplayLayoutPlugin(options) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
DisplayLayoutPlugin._installed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,10 +42,7 @@
|
||||
|
||||
<!-- Checkbox list, NOT editing -->
|
||||
<template v-if="filter.possibleValues && !isEditing">
|
||||
<span
|
||||
v-if="persistedFilters[filter.comparator]">
|
||||
{{persistedFilters[filter.comparator].join(', ')}}
|
||||
</span>
|
||||
<span>{{persistedFilters[filter.comparator].join(', ')}}</span>
|
||||
</template>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
@@ -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="c-object-label">
|
||||
<div class="c-object-label__type-icon"
|
||||
<div class="t-object-label l-flex-row flex-elem grows">
|
||||
<div class="t-item-icon flex-elem"
|
||||
:class="objectCssClass">
|
||||
</div>
|
||||
<div class="c-object-label__name flex-elem grows">{{ filterObject.name }}</div>
|
||||
<div class="t-title-label flex-elem grows">{{ filterObject.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -69,17 +69,16 @@ export default {
|
||||
}
|
||||
} else {
|
||||
if (!this.updatedFilters[key]) {
|
||||
this.$set(this.updatedFilters, key, {});
|
||||
this.updatedFilters[key] = {};
|
||||
}
|
||||
this.$set(this.updatedFilters[key], comparator, [value ? valueName : undefined]);
|
||||
this.updatedFilters[key][comparator] = [value ? valueName : undefined];
|
||||
}
|
||||
|
||||
this.$emit('updateFilters', this.keyString, this.updatedFilters);
|
||||
},
|
||||
updateTextFilter(key, comparator, value) {
|
||||
if (!this.updatedFilters[key]) {
|
||||
this.$set(this.updatedFilters, key, {});
|
||||
this.$set(this.updatedFilters[key], comparator, '');
|
||||
this.updatedFilters[key] = {};
|
||||
}
|
||||
this.updatedFilters[key][comparator] = value;
|
||||
this.$emit('updateFilters', this.keyString, this.updatedFilters);
|
||||
|
||||
@@ -23,18 +23,17 @@ export default {
|
||||
FilterObject
|
||||
},
|
||||
inject: [
|
||||
'openmct'
|
||||
'openmct',
|
||||
'providedObject'
|
||||
],
|
||||
data() {
|
||||
let providedObject = this.openmct.selection.get()[0][0].context.item;
|
||||
let persistedFilters = {};
|
||||
|
||||
if (providedObject.configuration && providedObject.configuration.filters) {
|
||||
persistedFilters = providedObject.configuration.filters;
|
||||
if (this.providedObject.configuration && this.providedObject.configuration.filters) {
|
||||
persistedFilters = this.providedObject.configuration.filters;
|
||||
}
|
||||
|
||||
return {
|
||||
providedObject,
|
||||
persistedFilters,
|
||||
children: {}
|
||||
}
|
||||
@@ -74,14 +73,13 @@ export default {
|
||||
this.composition.on('add', this.addChildren);
|
||||
this.composition.on('remove', this.removeChildren);
|
||||
this.composition.load();
|
||||
|
||||
this.unobserve = this.openmct.objects.observe(this.providedObject, 'configuration.filters', this.updatePersistedFilters);
|
||||
this.unobserveAllMutation = this.openmct.objects.observe(this.providedObject, '*', (mutatedObject) => this.providedObject = mutatedObject);
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.composition.off('add', this.addChildren);
|
||||
this.composition.off('remove', this.removeChildren);
|
||||
this.unobserve();
|
||||
this.unobserveAllMutation();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -33,20 +33,23 @@ define([
|
||||
key: 'filters-inspector',
|
||||
name: 'Filters Inspector View',
|
||||
canView: function (selection) {
|
||||
if (selection.length === 0 || selection[0].length === 0) {
|
||||
if (selection.length === 0) {
|
||||
return false;
|
||||
}
|
||||
let object = selection[0][0].context.item;
|
||||
let object = selection[0].context.item;
|
||||
|
||||
return object && supportedObjectTypesArray.some(type => object.type === type);
|
||||
},
|
||||
view: function (selection) {
|
||||
let component;
|
||||
let providedObject = selection[0].context.item;
|
||||
|
||||
return {
|
||||
show: function (element) {
|
||||
component = new Vue({
|
||||
provide: {
|
||||
openmct
|
||||
openmct,
|
||||
providedObject
|
||||
},
|
||||
components: {
|
||||
FiltersView: FiltersView.default
|
||||
@@ -56,10 +59,8 @@ define([
|
||||
});
|
||||
},
|
||||
destroy: function () {
|
||||
if (component) {
|
||||
component.$destroy();
|
||||
component = undefined;
|
||||
}
|
||||
component.$destroy();
|
||||
component = undefined;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -106,6 +106,9 @@
|
||||
.c-fl {
|
||||
@include abs();
|
||||
display: flex;
|
||||
flex-direction: column; // TEMP: only needed to support temp-toolbar element
|
||||
|
||||
> * + * { margin-top: $interiorMargin; }
|
||||
|
||||
.temp-toolbar {
|
||||
flex: 0 0 auto;
|
||||
@@ -113,8 +116,7 @@
|
||||
|
||||
&__container-holder {
|
||||
display: flex;
|
||||
flex: 1 1 100%; // Must be 100% to work
|
||||
overflow: auto;
|
||||
flex: 1 1 100%; // Must needs to be 100% to work
|
||||
|
||||
// Columns by default
|
||||
flex-direction: row;
|
||||
@@ -290,6 +292,11 @@
|
||||
margin-bottom: $interiorMargin;
|
||||
}
|
||||
|
||||
&__object-view {
|
||||
flex: 1 1 auto;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
&__size-indicator {
|
||||
$size: 35px;
|
||||
|
||||
@@ -415,7 +422,6 @@ import Container from '../utils/container';
|
||||
import Frame from '../utils/frame';
|
||||
import ResizeHandle from './resizeHandle.vue';
|
||||
import DropHint from './dropHint.vue';
|
||||
import RemoveAction from '../../remove/RemoveAction.js';
|
||||
|
||||
const MIN_CONTAINER_SIZE = 5;
|
||||
|
||||
@@ -507,7 +513,7 @@ export default {
|
||||
remove associated domainObjects from composition
|
||||
*/
|
||||
container.frames.forEach(f => {
|
||||
this.removeFromComposition(f.domainObjectIdentifier);
|
||||
this.composition.remove({identifier: f.domainObjectIdentifier});
|
||||
});
|
||||
|
||||
this.containers.splice(containerIndex, 1);
|
||||
@@ -522,7 +528,6 @@ export default {
|
||||
}
|
||||
|
||||
sizeToFill(this.containers);
|
||||
this.setSelectionToParent();
|
||||
this.persist();
|
||||
},
|
||||
moveFrame(toContainerIndex, toFrameIndex, frameId, fromContainerIndex) {
|
||||
@@ -556,23 +561,20 @@ export default {
|
||||
deleteFrame(frameId) {
|
||||
let container = this.containers
|
||||
.filter(c => c.frames.some(f => f.id === frameId))[0];
|
||||
let containerIndex = this.containers.indexOf(container);
|
||||
let frame = container
|
||||
.frames
|
||||
.filter((f => f.id === frameId))[0];
|
||||
let frameIndex = container.frames.indexOf(frame);
|
||||
|
||||
this.removeFromComposition(frame.domainObjectIdentifier)
|
||||
.then(() => {
|
||||
sizeToFill(container.frames)
|
||||
this.setSelectionToParent();
|
||||
});
|
||||
},
|
||||
removeFromComposition(identifier) {
|
||||
return this.openmct.objects.get(identifier).then((childDomainObject) => {
|
||||
this.RemoveAction.removeFromComposition(this.domainObject, childDomainObject);
|
||||
});
|
||||
},
|
||||
setSelectionToParent() {
|
||||
this.$el.click();
|
||||
/*
|
||||
remove associated domainObject from composition
|
||||
*/
|
||||
this.composition.remove({identifier: frame.domainObjectIdentifier});
|
||||
|
||||
container.frames.splice(frameIndex, 1);
|
||||
sizeToFill(container.frames);
|
||||
this.persist(containerIndex);
|
||||
},
|
||||
allowContainerDrop(event, index) {
|
||||
if (!event.dataTransfer.types.includes('containerid')) {
|
||||
@@ -663,8 +665,6 @@ export default {
|
||||
this.composition.on('remove', this.removeChildObject);
|
||||
this.composition.on('add', this.addFrame);
|
||||
|
||||
this.RemoveAction = new RemoveAction(this.openmct);
|
||||
|
||||
this.unobserve = this.openmct.objects.observe(this.domainObject, '*', this.updateDomainObject);
|
||||
},
|
||||
beforeDestroy() {
|
||||
|
||||
@@ -79,14 +79,12 @@ export default {
|
||||
},
|
||||
setSelection() {
|
||||
this.$nextTick(function () {
|
||||
if (this.$refs && this.$refs.objectFrame) {
|
||||
let childContext = this.$refs.objectFrame.getSelectionContext();
|
||||
childContext.item = this.domainObject;
|
||||
childContext.type = 'frame';
|
||||
childContext.frameId = this.frame.id;
|
||||
this.unsubscribeSelection = this.openmct.selection.selectable(
|
||||
this.$refs.frame, childContext, false);
|
||||
}
|
||||
let childContext = this.$refs.objectFrame.getSelectionContext();
|
||||
childContext.item = this.domainObject;
|
||||
childContext.type = 'frame';
|
||||
childContext.frameId = this.frame.id;
|
||||
this.unsubscribeSelection = this.openmct.selection.selectable(
|
||||
this.$refs.frame, childContext, false);
|
||||
});
|
||||
},
|
||||
initDrag(event) {
|
||||
|
||||
@@ -79,12 +79,10 @@ export default {
|
||||
mounted() {
|
||||
document.addEventListener('dragstart', this.setDragging);
|
||||
document.addEventListener('dragend', this.unsetDragging);
|
||||
document.addEventListener('drop', this.unsetDragging);
|
||||
},
|
||||
destroyed() {
|
||||
document.removeEventListener('dragstart', this.setDragging);
|
||||
document.removeEventListener('dragend', this.unsetDragging);
|
||||
document.removeEventListener('drop', this.unsetDragging);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -27,22 +27,28 @@ function ToolbarProvider(openmct) {
|
||||
key: "flex-layout",
|
||||
description: "A toolbar for objects inside a Flexible Layout.",
|
||||
forSelection: function (selection) {
|
||||
let context = selection[0][0].context;
|
||||
let context = selection[0].context;
|
||||
|
||||
return (context && context.type &&
|
||||
(context.type === 'flexible-layout' || context.type === 'container' || context.type === 'frame'));
|
||||
},
|
||||
toolbar: function (selection) {
|
||||
|
||||
let selectionPath = selection[0],
|
||||
primary = selectionPath[0],
|
||||
secondary = selectionPath[1],
|
||||
tertiary = selectionPath[2],
|
||||
let primary = selection[0],
|
||||
secondary = selection[1],
|
||||
tertiary = selection[2],
|
||||
deleteFrame,
|
||||
toggleContainer,
|
||||
deleteContainer,
|
||||
addContainer,
|
||||
toggleFrame;
|
||||
toggleFrame,
|
||||
separator;
|
||||
|
||||
separator = {
|
||||
control: "separator",
|
||||
domainObject: selection[0].context.item,
|
||||
key: "separator"
|
||||
};
|
||||
|
||||
toggleContainer = {
|
||||
control: 'toggle-button',
|
||||
@@ -63,12 +69,6 @@ function ToolbarProvider(openmct) {
|
||||
]
|
||||
};
|
||||
|
||||
function getSeparator() {
|
||||
return {
|
||||
control: "separator"
|
||||
};
|
||||
}
|
||||
|
||||
if (primary.context.type === 'frame') {
|
||||
let frameId = primary.context.frameId;
|
||||
let layoutObject = tertiary.context.item;
|
||||
@@ -77,11 +77,11 @@ function ToolbarProvider(openmct) {
|
||||
.containers;
|
||||
let container = containers
|
||||
.filter(c => c.frames.some(f => f.id === frameId))[0];
|
||||
let containerIndex = containers.indexOf(container);
|
||||
let frame = container && container
|
||||
let frame = container
|
||||
.frames
|
||||
.filter((f => f.id === frameId))[0];
|
||||
let frameIndex = container && container.frames.indexOf(frame);
|
||||
let containerIndex = containers.indexOf(container);
|
||||
let frameIndex = container.frames.indexOf(frame);
|
||||
|
||||
deleteFrame = {
|
||||
control: "button",
|
||||
@@ -202,9 +202,9 @@ function ToolbarProvider(openmct) {
|
||||
let toolbar = [
|
||||
toggleContainer,
|
||||
addContainer,
|
||||
toggleFrame ? getSeparator() : undefined,
|
||||
toggleFrame ? separator: undefined,
|
||||
toggleFrame,
|
||||
deleteFrame || deleteContainer ? getSeparator() : undefined,
|
||||
deleteFrame || deleteContainer ? separator : undefined,
|
||||
deleteFrame,
|
||||
deleteContainer
|
||||
];
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
</div>
|
||||
<div class="c-grid-item__controls">
|
||||
<div class="icon-people" title='Shared'></div>
|
||||
<button class="c-icon-button icon-info c-info-button" title='More Info'></button>
|
||||
<button class="c-click-icon icon-info c-info-button" title='More Info'></button>
|
||||
<div class="icon-pointer-right c-pointer-icon"></div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
@@ -14,7 +14,6 @@ export default {
|
||||
},
|
||||
mounted() {
|
||||
this.composition = this.openmct.composition.get(this.domainObject);
|
||||
this.keystring = this.openmct.objects.makeKeyString(this.domainObject.identifier);
|
||||
if (!this.composition) {
|
||||
return;
|
||||
}
|
||||
@@ -35,7 +34,7 @@ export default {
|
||||
this.items.push({
|
||||
model: child,
|
||||
type: type.definition,
|
||||
isAlias: this.keystring !== child.location,
|
||||
isAlias: this.domainObject.identifier.key !== child.location,
|
||||
objectPath: [child].concat(this.openmct.router.path),
|
||||
objectKeyString: this.openmct.objects.makeKeyString(child.identifier)
|
||||
});
|
||||
|
||||
@@ -1,53 +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.
|
||||
*****************************************************************************/
|
||||
|
||||
export default class GoToOriginalAction {
|
||||
constructor(openmct) {
|
||||
this.name = 'Go To Original';
|
||||
this.description = 'Go to the original unlinked instance of this object';
|
||||
|
||||
this._openmct = openmct;
|
||||
}
|
||||
invoke(objectPath) {
|
||||
this._openmct.objects.getOriginalPath(objectPath[0].identifier)
|
||||
.then((originalPath) => {
|
||||
let url = '#/browse/' + originalPath
|
||||
.map(function (o) {
|
||||
return o && this._openmct.objects.makeKeyString(o.identifier);
|
||||
}.bind(this))
|
||||
.reverse()
|
||||
.slice(1)
|
||||
.join('/');
|
||||
|
||||
window.location.href = url;
|
||||
});
|
||||
}
|
||||
appliesTo(objectPath) {
|
||||
let parentKeystring = objectPath[1] && this._openmct.objects.makeKeyString(objectPath[1].identifier);
|
||||
|
||||
if (!parentKeystring) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (parentKeystring !== objectPath[0].location);
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@
|
||||
</div>
|
||||
|
||||
<div class="c-ne__local-controls--hidden">
|
||||
<button class="c-icon-button c-icon-button--major icon-trash"
|
||||
<button class="c-click-icon c-click-icon--major icon-trash"
|
||||
title="Delete this entry"
|
||||
@click="deleteEntry">
|
||||
</button>
|
||||
|
||||
@@ -25,235 +25,176 @@ define([
|
||||
], function (
|
||||
uuid
|
||||
) {
|
||||
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 {
|
||||
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 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 = [];
|
||||
|
||||
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];
|
||||
let identifier = undefined;
|
||||
|
||||
if (isTelemetry(domainObject)) {
|
||||
// If object is a telemetry point, convert it to a plot and
|
||||
// replace the object in migratedObject composition with the plot.
|
||||
identifier = {
|
||||
key: uuid(),
|
||||
namespace: migratedObject.identifier.namespace
|
||||
};
|
||||
let plotObject = {
|
||||
identifier: identifier,
|
||||
location: domainObject.location,
|
||||
name: domainObject.name,
|
||||
type: "telemetry.plot.overlay"
|
||||
};
|
||||
let plotType = openmct.types.get('telemetry.plot.overlay');
|
||||
plotType.definition.initialize(plotObject);
|
||||
plotObject.composition.push(domainObject.identifier);
|
||||
openmct.objects.mutate(plotObject, 'persisted', Date.now());
|
||||
|
||||
let keyString = openmct.objects.makeKeyString(domainObject.identifier);
|
||||
let clonedComposition = Object.assign([], migratedObject.composition);
|
||||
clonedComposition.forEach((identifier, index) => {
|
||||
if (openmct.objects.makeKeyString(identifier) === keyString) {
|
||||
migratedObject.composition[index] = plotObject.identifier;
|
||||
}
|
||||
});
|
||||
}
|
||||
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],
|
||||
identifier: identifier || domainObject.identifier,
|
||||
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, gridSize) {
|
||||
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,
|
||||
id: uuid()
|
||||
};
|
||||
|
||||
if (!element.useGrid) {
|
||||
item.x = Math.round(item.x / gridSize[0]);
|
||||
item.y = Math.round(item.y / gridSize[1]);
|
||||
item.width = Math.round(item.width / gridSize[0]);
|
||||
item.height = Math.round(item.height / gridSize[1]);
|
||||
}
|
||||
|
||||
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 gridSize = domainObject.layoutGrid || DEFAULT_GRID_SIZE;
|
||||
let layoutType = openmct.types.get('layout');
|
||||
layoutType.definition.initialize(newLayoutObject);
|
||||
newLayoutObject.composition = domainObject.composition;
|
||||
newLayoutObject.configuration.layoutGrid = gridSize;
|
||||
|
||||
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, gridSize);
|
||||
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;
|
||||
});
|
||||
}
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
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;
|
||||
});
|
||||
}
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
@@ -20,21 +20,19 @@
|
||||
* 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])
|
||||
@@ -48,6 +46,6 @@ export default function () {
|
||||
}
|
||||
return object;
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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 has-local-controls has-cursor-guides">
|
||||
<div class="gl-plot-display-area">
|
||||
<mct-ticks axis="xAxis">
|
||||
<div class="gl-plot-hash hash-v"
|
||||
ng-repeat="tick in ticks track by tick.value"
|
||||
@@ -147,6 +147,7 @@
|
||||
</div>
|
||||
</mct-ticks>
|
||||
|
||||
|
||||
<mct-ticks axis="yAxis">
|
||||
<div class="gl-plot-hash hash-h"
|
||||
ng-repeat="tick in ticks track by tick.value"
|
||||
@@ -154,6 +155,7 @@
|
||||
</div>
|
||||
</mct-ticks>
|
||||
|
||||
|
||||
<mct-chart config="config"
|
||||
series="series"
|
||||
rectangles="rectangles"
|
||||
@@ -162,37 +164,23 @@
|
||||
the-y-axis="yAxis">
|
||||
</mct-chart>
|
||||
|
||||
<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 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>
|
||||
</div>
|
||||
|
||||
<!--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>
|
||||
<span class="t-wait-spinner loading" ng-show="plot.isRequestPending()">
|
||||
</span>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="gl-plot-axis-area gl-plot-x"
|
||||
|
||||
@@ -20,34 +20,31 @@
|
||||
at runtime from the About dialog for additional information.
|
||||
-->
|
||||
<span ng-controller="PlotController as controller"
|
||||
class="abs holder holder-plot has-control-bar">
|
||||
class="abs holder holder-plot has-control-bar"
|
||||
ng-class="{
|
||||
'loading': !!pending
|
||||
}"
|
||||
>
|
||||
<div class="l-control-bar" ng-show="!controller.hideExportButtons">
|
||||
<span class="c-button-set c-button-set--strip-h">
|
||||
<button class="c-button icon-download"
|
||||
<span class="c-button-set c-button-set--strip">
|
||||
<a class="c-button icon-download"
|
||||
ng-click="controller.exportPNG()"
|
||||
title="Export This View's Data as PNG">
|
||||
<span class="c-button__label">PNG</span>
|
||||
</button>
|
||||
<button class="c-button"
|
||||
</a>
|
||||
<a class="c-button"
|
||||
ng-click="controller.exportJPG()"
|
||||
title="Export This View's Data as JPG">
|
||||
<span class="c-button__label">JPG</span>
|
||||
</button>
|
||||
</a>
|
||||
</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>
|
||||
|
||||
@@ -20,39 +20,32 @@
|
||||
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">
|
||||
class="abs holder holder-plot has-control-bar t-plot-stacked"
|
||||
ng-class="{
|
||||
'loading': !!currentRequest.pending
|
||||
}">
|
||||
|
||||
<div class="l-control-bar" ng-show="!stackedPlot.hideExportButtons">
|
||||
<span class="c-button-set c-button-set--strip-h">
|
||||
<button class="c-button icon-download"
|
||||
<span class="c-button-set c-button-set--strip">
|
||||
<a class="c-button icon-download"
|
||||
ng-click="stackedPlot.exportPNG()"
|
||||
title="Export This View's Data as PNG">
|
||||
<span class="c-button__label">PNG</span>
|
||||
</button>
|
||||
<button class="c-button"
|
||||
</a>
|
||||
<a class="c-button"
|
||||
ng-click="stackedPlot.exportJPG()"
|
||||
title="Export This View's Data as JPG">
|
||||
<span class="c-button__label">JPG</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>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
<div class="l-view-section">
|
||||
<div class="c-loading--overlay loading"
|
||||
ng-show="!!currentRequest.pending"></div>
|
||||
<div class="gl-plot child-frame u-inspectable"
|
||||
<div class="gl-plot child-frame"
|
||||
ng-repeat="telemetryObject in telemetryObjects"
|
||||
ng-class="{
|
||||
's-status-timeconductor-unsynced': telemetryObject
|
||||
.getCapability('status')
|
||||
.get('timeconductor-unsynced')
|
||||
}"
|
||||
mct-selectable="{
|
||||
item: telemetryObject.useCapability('adapter'),
|
||||
oldItem: telemetryObject
|
||||
}">
|
||||
<mct-overlay-plot domain-object="telemetryObject"></mct-overlay-plot>
|
||||
</div>
|
||||
|
||||
@@ -70,15 +70,13 @@ define([
|
||||
this.listenTo(this.$scope, '$destroy', this.destroy, this);
|
||||
this.listenTo(config.series, 'add', this.addSeries, this);
|
||||
this.listenTo(config.series, 'remove', this.resetAllSeries, this);
|
||||
|
||||
config.series.forEach(this.addSeries, this);
|
||||
};
|
||||
|
||||
PlotOptionsController.prototype.addSeries = function (series, index) {
|
||||
this.$timeout(function () {
|
||||
this.$scope.plotSeries[index] = series;
|
||||
series.locateOldObject(this.$scope.domainObject);
|
||||
}.bind(this));
|
||||
this.$scope.plotSeries[index] = series;
|
||||
series.locateOldObject(this.$scope.domainObject);
|
||||
|
||||
};
|
||||
|
||||
PlotOptionsController.prototype.resetAllSeries = function (series, index) {
|
||||
|
||||
@@ -78,7 +78,6 @@ 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();
|
||||
|
||||
@@ -93,12 +92,6 @@ 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);
|
||||
@@ -150,9 +143,6 @@ define([
|
||||
y: this.yScale.invert(this.positionOverElement.y)
|
||||
};
|
||||
|
||||
if (this.cursorGuide) {
|
||||
this.updateCrosshairs($event);
|
||||
}
|
||||
this.highlightValues(this.positionOverPlot.x);
|
||||
this.updateMarquee();
|
||||
this.updatePan();
|
||||
@@ -160,11 +150,6 @@ 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();
|
||||
@@ -281,103 +266,6 @@ 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();
|
||||
@@ -474,9 +362,5 @@ define([
|
||||
this.stopListening();
|
||||
};
|
||||
|
||||
MCTPlotController.prototype.toggleCursorGuide = function ($event) {
|
||||
this.cursorGuide = !this.cursorGuide;
|
||||
};
|
||||
|
||||
return MCTPlotController;
|
||||
});
|
||||
|
||||
@@ -59,7 +59,6 @@ define([
|
||||
this.openmct = openmct;
|
||||
this.objectService = objectService;
|
||||
this.exportImageService = exportImageService;
|
||||
this.cursorGuide = false;
|
||||
|
||||
$scope.pending = 0;
|
||||
|
||||
@@ -219,7 +218,6 @@ define([
|
||||
|
||||
PlotController.prototype.stopLoading = function () {
|
||||
this.$scope.pending -= 1;
|
||||
this.$scope.$digest();
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -260,8 +258,8 @@ define([
|
||||
PlotController.prototype.updateFiltersAndResubscribe = function (updatedFilters) {
|
||||
this.config.series.forEach(function (series) {
|
||||
series.updateFiltersAndRefresh(updatedFilters[series.keyString]);
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Export view as JPG.
|
||||
@@ -285,11 +283,6 @@ define([
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
PlotController.prototype.toggleCursorGuide = function ($event) {
|
||||
this.cursorGuide = !this.cursorGuide;
|
||||
this.$scope.$broadcast('cursorguide', $event);
|
||||
};
|
||||
|
||||
return PlotController;
|
||||
|
||||
});
|
||||
|
||||
@@ -35,8 +35,6 @@ define([
|
||||
|
||||
this.$element = $element;
|
||||
this.exportImageService = exportImageService;
|
||||
this.$scope = $scope;
|
||||
this.cursorGuide = false;
|
||||
|
||||
$scope.telemetryObjects = [];
|
||||
|
||||
@@ -79,15 +77,6 @@ define([
|
||||
$scope.$broadcast('plot:tickWidth', _.max(tickWidthMap));
|
||||
}
|
||||
}
|
||||
|
||||
function compositionReorder(reorderPlan) {
|
||||
let oldComposition = telemetryObjects.slice();
|
||||
|
||||
reorderPlan.forEach((reorder) => {
|
||||
telemetryObjects[reorder.newIndex] = oldComposition[reorder.oldIndex];
|
||||
});
|
||||
}
|
||||
|
||||
thisRequest.pending += 1;
|
||||
openmct.objects.get(domainObject.getId())
|
||||
.then(function (obj) {
|
||||
@@ -98,12 +87,10 @@ define([
|
||||
composition = openmct.composition.get(obj);
|
||||
composition.on('add', addChild);
|
||||
composition.on('remove', removeChild);
|
||||
composition.on('reorder', compositionReorder);
|
||||
composition.load();
|
||||
unlisten = function () {
|
||||
composition.off('add', addChild);
|
||||
composition.off('remove', removeChild);
|
||||
composition.off('reorder', compositionReorder);
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -158,10 +145,5 @@ define([
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
StackedPlotController.prototype.toggleCursorGuide = function ($event) {
|
||||
this.cursorGuide = !this.cursorGuide;
|
||||
this.$scope.$broadcast('cursorguide', $event);
|
||||
};
|
||||
|
||||
return StackedPlotController;
|
||||
});
|
||||
|
||||
@@ -41,8 +41,7 @@ define([
|
||||
'./tabs/plugin',
|
||||
'./LADTable/plugin',
|
||||
'./filters/plugin',
|
||||
'./objectMigration/plugin',
|
||||
'./goToOriginalAction/plugin'
|
||||
'./objectMigration/plugin'
|
||||
], function (
|
||||
_,
|
||||
UTCTimeSystem,
|
||||
@@ -64,8 +63,7 @@ define([
|
||||
Tabs,
|
||||
LADTable,
|
||||
Filters,
|
||||
ObjectMigration,
|
||||
GoToOriginalAction
|
||||
ObjectMigration
|
||||
) {
|
||||
var bundleMap = {
|
||||
LocalStorage: 'platform/persistence/local',
|
||||
@@ -162,7 +160,6 @@ define([
|
||||
plugins.LADTable = LADTable;
|
||||
plugins.Filters = Filters;
|
||||
plugins.ObjectMigration = ObjectMigration.default;
|
||||
plugins.GoToOriginalAction = GoToOriginalAction.default;
|
||||
|
||||
return plugins;
|
||||
});
|
||||
|
||||
@@ -85,17 +85,16 @@ export default class RemoveAction {
|
||||
);
|
||||
|
||||
this.openmct.objects.mutate(parent, 'composition', composition);
|
||||
|
||||
if (this.inNavigationPath(child) && this.openmct.editor.isEditing()) {
|
||||
this.openmct.editor.save();
|
||||
}
|
||||
}
|
||||
|
||||
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 parentType &&
|
||||
return objectType.definition.creatable &&
|
||||
parentType &&
|
||||
parentType.definition.creatable &&
|
||||
Array.isArray(parent.composition);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<span class="t-configuration"> </span>
|
||||
<span class="t-value-inputs"> </span>
|
||||
</span>
|
||||
<span class="flex-elem c-local-controls--show-on-hover l-condition-action-buttons-wrapper">
|
||||
<span class="flex-elem local-control local-controls-hidden 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>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<div class="c-sw-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 l-compact-form has-local-controls l-widget-rule s-widget-rule">
|
||||
<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 c-local-controls--show-on-hover l-rule-action-buttons-wrapper">
|
||||
<div class="flex-elem local-control local-controls-hidden 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>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<span class="equal-to hidden"> equal to </span>
|
||||
<span class="t-value-inputs"></span>
|
||||
</span>
|
||||
<span class="flex-elem c-local-controls--show-on-hover l-widget-test-data-item-action-buttons-wrapper">
|
||||
<span class="flex-elem local-control local-controls-hidden 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>
|
||||
|
||||
@@ -3,9 +3,11 @@
|
||||
<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="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 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>
|
||||
</div>
|
||||
<div class="c-sw-edit__ui holder l-flex-accordion flex-elem grows widget-edit-holder expanded-widget-test-data expanded-widget-rules">
|
||||
|
||||
@@ -237,7 +237,6 @@ define ([
|
||||
});
|
||||
delete this.compositionObjs[objectId];
|
||||
this.subscriptions[objectId](); //unsubscribe from telemetry source
|
||||
delete this.subscriptions[objectId];
|
||||
this.eventEmitter.emit('remove', identifier);
|
||||
|
||||
if (_.isEmpty(this.compositionObjs)) {
|
||||
|
||||
@@ -43,7 +43,7 @@ define([
|
||||
return evaluator.requestLatest(options)
|
||||
.then(function (latestDatum) {
|
||||
this.pool.release(evaluator);
|
||||
return latestDatum ? [latestDatum] : [];
|
||||
return [latestDatum];
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
|
||||
@@ -52,10 +52,7 @@ define([
|
||||
strategy: 'latest',
|
||||
size: 1
|
||||
}).then(function (results) {
|
||||
if (this.destroyed ||
|
||||
this.hasUpdated ||
|
||||
this.renderTracker !== renderTracker ||
|
||||
results.length === 0) {
|
||||
if (this.destroyed || this.hasUpdated || this.renderTracker !== renderTracker) {
|
||||
return;
|
||||
}
|
||||
this.updateState(results[results.length - 1]);
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
<template>
|
||||
<div class="c-tabs-view">
|
||||
<div class="c-tabs-view__tabs-holder c-tabs"
|
||||
<div class="c-tabs-view__tabs-holder c-compact-button-holder"
|
||||
:class="{
|
||||
'is-dragging': isDragging,
|
||||
'is-mouse-over': allowDrop
|
||||
}">
|
||||
<div class="c-drop-hint"
|
||||
@drop="onDrop"
|
||||
@dragenter="dragenter"
|
||||
@dragleave="dragleave">
|
||||
ref="dropHint">
|
||||
</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-tab"
|
||||
<button class="c-tabs-view__tab c-compact-button"
|
||||
v-for="(tab,index) in tabsList"
|
||||
:key="index"
|
||||
:class="[
|
||||
@@ -26,9 +25,8 @@
|
||||
<div class="c-tabs-view__object-holder"
|
||||
v-for="(tab, index) in tabsList"
|
||||
:key="index"
|
||||
:class="{'c-tabs-view__object-holder--hidden': !isCurrent(tab)}">
|
||||
<div v-if="currentTab"
|
||||
class="c-tabs-view__object-name l-browse-bar__object-name--w"
|
||||
:class="{'invisible': !isCurrent(tab)}">
|
||||
<div 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}}
|
||||
@@ -55,28 +53,15 @@
|
||||
}
|
||||
|
||||
&__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;
|
||||
flex-direction: column;
|
||||
|
||||
&--hidden {
|
||||
height: 1000px;
|
||||
width: 1000px;
|
||||
position: absolute;
|
||||
left: -9999px;
|
||||
top: -9999px;
|
||||
}
|
||||
}
|
||||
|
||||
&__object-name {
|
||||
@@ -87,14 +72,10 @@
|
||||
}
|
||||
|
||||
&__object {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
flex: 1 1 auto;
|
||||
height: 0; // Chrome 73 oveflow bug fix
|
||||
}
|
||||
|
||||
&__empty-message {
|
||||
background: rgba($colorBodyFg, 0.1);
|
||||
color: rgba($colorBodyFg, 0.7);
|
||||
font-style: italic;
|
||||
text-align: center;
|
||||
@@ -159,13 +140,6 @@ 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;
|
||||
},
|
||||
@@ -192,20 +166,31 @@ 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();
|
||||
}
|
||||
|
||||
document.addEventListener('dragstart', this.dragstart);
|
||||
document.addEventListener('dragend', this.dragend);
|
||||
|
||||
let dropHint = this.$refs.dropHint;
|
||||
|
||||
if (dropHint) {
|
||||
dropHint.addEventListener('dragenter', this.dragenter);
|
||||
dropHint.addEventListener('dragleave', this.dragleave);
|
||||
}
|
||||
},
|
||||
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);
|
||||
},
|
||||
beforeDestroy() {
|
||||
let dropHint = this.$refs.dropHint;
|
||||
|
||||
dropHint.removeEventListener('dragenter', this.dragenter);
|
||||
dropHint.removeEventListener('dragleave', this.dragleave);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -31,7 +31,6 @@ define([
|
||||
|
||||
openmct.types.addType('tabs', {
|
||||
name: "Tabs View",
|
||||
description: 'Add multiple objects of any type to this view, and quickly navigate between them with tabs',
|
||||
creatable: true,
|
||||
cssClass: 'icon-tabs-view',
|
||||
initialize(domainObject) {
|
||||
|
||||
@@ -37,15 +37,15 @@ define([
|
||||
key: 'table-configuration',
|
||||
name: 'Telemetry Table Configuration',
|
||||
canView: function (selection) {
|
||||
if (selection.length === 0 || selection[0].length === 0) {
|
||||
if (selection.length === 0) {
|
||||
return false;
|
||||
}
|
||||
let object = selection[0][0].context.item;
|
||||
let object = selection[0].context.item;
|
||||
return object && object.type === 'table';
|
||||
},
|
||||
view: function (selection) {
|
||||
let component;
|
||||
let domainObject = selection[0][0].context.item;
|
||||
let domainObject = selection[0].context.item;
|
||||
let tableConfiguration = new TelemetryTableConfiguration(domainObject, openmct);
|
||||
return {
|
||||
show: function (element) {
|
||||
@@ -62,11 +62,8 @@ define([
|
||||
});
|
||||
},
|
||||
destroy: function () {
|
||||
if (component) {
|
||||
component.$destroy();
|
||||
component = undefined;
|
||||
}
|
||||
|
||||
component.$destroy();
|
||||
component = undefined;
|
||||
tableConfiguration = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,9 +59,7 @@ define([
|
||||
this.filterObserver = undefined;
|
||||
|
||||
this.createTableRowCollections();
|
||||
|
||||
openmct.time.on('bounds', this.refreshData);
|
||||
openmct.time.on('timeSystem', this.refreshData);
|
||||
}
|
||||
|
||||
initialize() {
|
||||
@@ -75,17 +73,13 @@ define([
|
||||
|
||||
createTableRowCollections() {
|
||||
this.boundedRows = new BoundedTableRowCollection(this.openmct);
|
||||
|
||||
//By default, sort by current time system, ascending.
|
||||
this.filteredRows = new FilteredTableRowCollection(this.boundedRows);
|
||||
|
||||
//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 || {
|
||||
this.filteredRows.sortBy({
|
||||
key: this.openmct.time.timeSystem().key,
|
||||
direction: 'asc'
|
||||
};
|
||||
this.filteredRows.sortBy(sortOptions);
|
||||
});
|
||||
}
|
||||
|
||||
loadComposition() {
|
||||
@@ -137,17 +131,12 @@ define([
|
||||
let requestOptions = this.buildOptionsFromConfiguration(telemetryObject);
|
||||
return this.openmct.telemetry.request(telemetryObject, requestOptions)
|
||||
.then(telemetryData => {
|
||||
//Check that telemetry object has not been removed since telemetry was requested.
|
||||
if (!this.telemetryObjects.includes(telemetryObject)) {
|
||||
return;
|
||||
}
|
||||
let keyString = this.openmct.objects.makeKeyString(telemetryObject.identifier);
|
||||
let columnMap = this.getColumnMapForObject(keyString);
|
||||
let limitEvaluator = this.openmct.telemetry.limitEvaluator(telemetryObject);
|
||||
|
||||
let telemetryRows = telemetryData.map(datum => new TelemetryTableRow(datum, columnMap, keyString, limitEvaluator));
|
||||
this.boundedRows.add(telemetryRows);
|
||||
}).finally(() => {
|
||||
this.decrementOutstandingRequests();
|
||||
});
|
||||
}
|
||||
@@ -177,7 +166,6 @@ define([
|
||||
if (!isTick) {
|
||||
this.filteredRows.clear();
|
||||
this.boundedRows.clear();
|
||||
this.boundedRows.sortByTimeSystem(this.openmct.time.timeSystem());
|
||||
this.telemetryObjects.forEach(this.requestDataFor);
|
||||
}
|
||||
}
|
||||
@@ -198,10 +186,6 @@ define([
|
||||
let limitEvaluator = this.openmct.telemetry.limitEvaluator(telemetryObject);
|
||||
|
||||
this.subscriptions[keyString] = this.openmct.telemetry.subscribe(telemetryObject, (datum) => {
|
||||
//Check that telemetry object has not been removed since telemetry was requested.
|
||||
if (!this.telemetryObjects.includes(telemetryObject)) {
|
||||
return;
|
||||
}
|
||||
this.boundedRows.add(new TelemetryTableRow(datum, columnMap, keyString, limitEvaluator));
|
||||
}, subscribeOptions);
|
||||
}
|
||||
@@ -224,22 +208,11 @@ 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.off('timeSystem', this.refreshData);
|
||||
if (this.filterObserver) {
|
||||
this.filterObserver();
|
||||
}
|
||||
|
||||
@@ -32,20 +32,12 @@ 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' ||
|
||||
hasTelemetry(domainObject)
|
||||
return domainObject.type === 'table' || domainObject.hasOwnProperty('telemetry');
|
||||
},
|
||||
canEdit(domainObject) {
|
||||
return domainObject.type === 'table';
|
||||
|
||||
@@ -41,6 +41,7 @@ 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);
|
||||
@@ -50,8 +51,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 = this.parseTime(item.datum[this.sortOptions.key]) < this.lastBounds.start;
|
||||
let afterEndOfBounds = this.parseTime(item.datum[this.sortOptions.key]) > this.lastBounds.end;
|
||||
let beforeStartOfBounds = item.datum[this.sortOptions.key] < this.lastBounds.start;
|
||||
let afterEndOfBounds = item.datum[this.sortOptions.key] > this.lastBounds.end;
|
||||
|
||||
if (!afterEndOfBounds && !beforeStartOfBounds) {
|
||||
return super.addOne(item);
|
||||
@@ -63,12 +64,6 @@ 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'});
|
||||
}
|
||||
|
||||
@@ -136,6 +131,7 @@ define(
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.openmct.time.off('timeSystem', this.sortByTimeSystem);
|
||||
this.openmct.time.off('bounds', this.bounds);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,8 +127,7 @@ define(
|
||||
if (testRowValue > lastValue) {
|
||||
return this.rows.length;
|
||||
} else if (testRowValue === lastValue) {
|
||||
// Maintain stable sort
|
||||
return this.rows.length;
|
||||
return this.rows.length - 1;
|
||||
} else if (testRowValue <= firstValue) {
|
||||
return 0;
|
||||
} else {
|
||||
@@ -142,8 +141,7 @@ define(
|
||||
} else if (testRowValue < lastValue) {
|
||||
return this.rows.length;
|
||||
} else if (testRowValue === lastValue) {
|
||||
// Maintain stable sort
|
||||
return this.rows.length;
|
||||
return this.rows.length - 1;
|
||||
} else {
|
||||
// Use a custom comparison function to support descending sort.
|
||||
return lodashFunction(rows, testRow, (thisRow) => {
|
||||
|
||||
@@ -34,13 +34,39 @@
|
||||
isSortable ? 'is-sortable' : '',
|
||||
isSortable && sortOptions.key === headerKey ? 'is-sorting' : '',
|
||||
isSortable && sortOptions.direction].join(' ')">
|
||||
<div class="c-telemetry-table__resize-hitarea"
|
||||
<div v-if="isEditing" class="c-telemetry-table__resize-hotzone c-telemetry-table__resize-hotzone--right"
|
||||
@mousedown="resizeColumnStart"
|
||||
></div>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</th>
|
||||
</template>
|
||||
<style lang="scss">
|
||||
@import "~styles/sass-base";
|
||||
@import "~styles/table";
|
||||
|
||||
$hotzone-size: 6px;
|
||||
|
||||
.c-telemetry-table__headers__content {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.c-table.c-telemetry-table {
|
||||
.c-telemetry-table__resize-hotzone {
|
||||
display: block;
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
width: $hotzone-size;
|
||||
min-width: $hotzone-size;
|
||||
cursor: col-resize;
|
||||
border: none;
|
||||
right: 0px;
|
||||
margin-right: -$tabularTdPadLR - 1 - $hotzone-size / 2;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
import _ from 'lodash';
|
||||
const MOVE_COLUMN_DT_TYPE = 'movecolumnfromindex';
|
||||
|
||||
@@ -23,18 +23,18 @@
|
||||
<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">
|
||||
<button class="c-button icon-download labeled"
|
||||
<a 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>
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
<div v-if="isDropTargetActive" class="c-telemetry-table__drop-target" :style="dropTargetStyle"></div>
|
||||
<!-- Headers table -->
|
||||
<div class="c-telemetry-table__headers-w js-table__headers-w" ref="headersTable" :style="{ 'max-width': widthWithScroll}">
|
||||
<table class="c-table__headers c-telemetry-table__headers">
|
||||
<thead>
|
||||
<tr class="c-telemetry-table__headers__labels">
|
||||
<tr class="c-telemetry-table__headers__name">
|
||||
<table-column-header
|
||||
v-for="(title, key, headerIndex) in headers"
|
||||
:key="key"
|
||||
@@ -49,8 +49,7 @@
|
||||
:columnWidth="columnWidths[key]"
|
||||
:sortOptions="sortOptions"
|
||||
:isEditing="isEditing"
|
||||
><span class="c-telemetry-table__headers__label">{{title}}</span>
|
||||
</table-column-header>
|
||||
>{{title}}</table-column-header>
|
||||
</tr>
|
||||
<tr class="c-telemetry-table__headers__filter">
|
||||
<table-column-header
|
||||
@@ -143,7 +142,6 @@
|
||||
// Wraps __headers table
|
||||
flex: 0 0 auto;
|
||||
overflow: hidden;
|
||||
background: $colorTabHeaderBg;
|
||||
}
|
||||
|
||||
/******************************* TABLES */
|
||||
@@ -160,32 +158,6 @@
|
||||
thead {
|
||||
display: block;
|
||||
}
|
||||
|
||||
&__labels {
|
||||
// Top row, has labels
|
||||
.c-telemetry-table__headers__content {
|
||||
// Holds __label, sort indicator and resize-hitarea
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__headers__label {
|
||||
overflow: hidden;
|
||||
flex: 0 1 auto;
|
||||
}
|
||||
|
||||
&__resize-hitarea {
|
||||
// In table-column-header.vue
|
||||
@include abs();
|
||||
display: none; // Set to display: block in .is-editing section below
|
||||
left: auto; right: -1 * $tabularTdPadLR;
|
||||
width: $tableResizeColHitareaD;
|
||||
cursor: col-resize;
|
||||
transform: translateX(50%); // Move so this element sits over border between columns
|
||||
}
|
||||
|
||||
/******************************* ELEMENTS */
|
||||
@@ -200,7 +172,6 @@
|
||||
&__body-w {
|
||||
// Wraps __body table provides scrolling
|
||||
flex: 1 1 100%;
|
||||
height: 0; // Fixes Chrome 73 overflow bug
|
||||
overflow-x: auto;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
@@ -248,7 +219,7 @@
|
||||
|
||||
/******************************* EDITING */
|
||||
.is-editing {
|
||||
.c-telemetry-table__headers__labels {
|
||||
.c-telemetry-table__headers__name {
|
||||
th[draggable],
|
||||
th[draggable] > * {
|
||||
cursor: move;
|
||||
@@ -260,10 +231,6 @@
|
||||
> * { background: $b; }
|
||||
}
|
||||
}
|
||||
|
||||
.c-telemetry-table__resize-hitarea {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
/******************************* LEGACY */
|
||||
@@ -441,7 +408,7 @@ export default {
|
||||
direction: 'asc'
|
||||
}
|
||||
}
|
||||
this.table.sortBy(this.sortOptions);
|
||||
this.table.filteredRows.sortBy(this.sortOptions);
|
||||
},
|
||||
scroll() {
|
||||
if (!this.processingScroll) {
|
||||
@@ -588,18 +555,13 @@ export default {
|
||||
let el = this.$el;
|
||||
let width = el.clientWidth;
|
||||
let height = el.clientHeight;
|
||||
let scrollTop = this.scrollable.scrollTop;
|
||||
|
||||
this.resizePollHandle = setInterval(() => {
|
||||
if ((el.clientWidth !== width || el.clientHeight !== height) && this.isAutosizeEnabled) {
|
||||
this.calculateTableSize();
|
||||
// On some resize events scrollTop is reset to 0. Possibly due to a transition we're using?
|
||||
// Need to preserve scroll position in this case.
|
||||
this.scrollable.scrollTop = scrollTop;
|
||||
width = el.clientWidth;
|
||||
height = el.clientHeight;
|
||||
}
|
||||
scrollTop = this.scrollable.scrollTop;
|
||||
}, RESIZE_POLL_INTERVAL);
|
||||
},
|
||||
|
||||
|
||||
@@ -23,76 +23,73 @@
|
||||
<div class="c-conductor"
|
||||
:class="[isFixed ? 'is-fixed-mode' : 'is-realtime-mode']">
|
||||
<form class="u-contents" ref="conductorForm" @submit.prevent="updateTimeFromConductor">
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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 class="c-conductor__controls">
|
||||
<!-- Mode, time system menu buttons and duration slider -->
|
||||
<ConductorMode class="c-conductor__mode-select"></ConductorMode>
|
||||
@@ -116,17 +113,17 @@
|
||||
|
||||
/*********************************************** CONDUCTOR LAYOUT */
|
||||
.c-conductor {
|
||||
&__time-bounds {
|
||||
display: grid;
|
||||
grid-column-gap: $interiorMargin;
|
||||
grid-row-gap: $interiorMargin;
|
||||
align-items: center;
|
||||
display: grid;
|
||||
grid-column-gap: $interiorMargin;
|
||||
grid-row-gap: $interiorMargin;
|
||||
align-items: center;
|
||||
|
||||
// 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";
|
||||
}
|
||||
// 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";
|
||||
|
||||
&__mode-icon {
|
||||
grid-area: tc-mode-icon;
|
||||
@@ -166,10 +163,10 @@
|
||||
}
|
||||
|
||||
&.is-realtime-mode {
|
||||
.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";
|
||||
}
|
||||
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__end-fixed {
|
||||
grid-area: tc-updated;
|
||||
@@ -177,15 +174,9 @@
|
||||
}
|
||||
|
||||
body.phone.portrait & {
|
||||
.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
|
||||
}
|
||||
grid-row-gap: $interiorMargin;
|
||||
grid-template-rows: auto auto auto;
|
||||
grid-template-columns: 20px auto auto;
|
||||
|
||||
&__mode-icon {
|
||||
grid-row: 1;
|
||||
@@ -209,19 +200,17 @@
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.c-conductor__time-bounds {
|
||||
grid-template-areas:
|
||||
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 {
|
||||
.c-conductor__time-bounds {
|
||||
grid-template-areas:
|
||||
grid-template-areas:
|
||||
"tc-mode-icon tc-start tc-updated"
|
||||
"tc-mode-icon tc-end tc-end";
|
||||
}
|
||||
"tc-mode-icon tc-end tc-end"
|
||||
"tc-mode-icon tc-controls tc-controls";
|
||||
|
||||
.c-conductor__end-fixed {
|
||||
justify-content: flex-end;
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
*****************************************************************************/
|
||||
<template>
|
||||
<div class="c-ctrl-wrapper c-ctrl-wrapper--menus-up c-datetime-picker__wrapper" ref="calendarHolder">
|
||||
<a class="c-icon-button icon-calendar"
|
||||
<a class="c-click-icon 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-icon-button icon-arrow-left"
|
||||
<div class="c-pager__prev c-click-icon 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-icon-button icon-arrow-right"
|
||||
<div class="c-pager__next c-click-icon 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-icon-button {
|
||||
.c-click-icon {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,15 +20,7 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
[
|
||||
'EventEmitter',
|
||||
'lodash'
|
||||
],
|
||||
function (
|
||||
EventEmitter,
|
||||
_
|
||||
) {
|
||||
define(['EventEmitter'], function (EventEmitter) {
|
||||
|
||||
/**
|
||||
* Manages selection state for Open MCT
|
||||
@@ -55,87 +47,21 @@ define(
|
||||
* Selects the selectable object and emits the 'change' event.
|
||||
*
|
||||
* @param {object} selectable an object with element and context properties
|
||||
* @param {Boolean} isMultiSelectEvent flag indication shift key is pressed or not
|
||||
* @private
|
||||
*/
|
||||
Selection.prototype.select = function (selectable, isMultiSelectEvent) {
|
||||
Selection.prototype.select = function (selectable) {
|
||||
if (!Array.isArray(selectable)) {
|
||||
selectable = [selectable];
|
||||
}
|
||||
|
||||
let multiSelect = isMultiSelectEvent &&
|
||||
this.parentSupportsMultiSelect(selectable) &&
|
||||
this.isPeer(selectable) &&
|
||||
!this.selectionContainsParent(selectable);
|
||||
|
||||
if (multiSelect) {
|
||||
this.handleMultiSelect(selectable);
|
||||
} else {
|
||||
this.setSelectionStyles(selectable);
|
||||
this.selected = [selectable];
|
||||
if (this.selected[0] && this.selected[0].element) {
|
||||
this.selected[0].element.removeAttribute('s-selected');
|
||||
}
|
||||
|
||||
this.emit('change', this.selected);
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
Selection.prototype.handleMultiSelect = function (selectable) {
|
||||
if (this.elementSelected(selectable)) {
|
||||
this.remove(selectable);
|
||||
} else {
|
||||
this.addSelectionAttributes(selectable);
|
||||
this.selected.push(selectable);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
Selection.prototype.elementSelected = function (selectable) {
|
||||
return this.selected.some(selectionPath => _.isEqual(selectionPath, selectable));
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
Selection.prototype.remove = function (selectable) {
|
||||
this.selected = this.selected.filter(selectionPath => !_.isEqual(selectionPath, selectable));
|
||||
|
||||
if (this.selected.length === 0) {
|
||||
this.removeSelectionAttributes(selectable);
|
||||
selectable[1].element.click(); // Select the parent if there is no selection.
|
||||
} else {
|
||||
this.removeSelectionAttributes(selectable, true);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
Selection.prototype.setSelectionStyles = function (selectable) {
|
||||
this.selected.map(selectionPath => {
|
||||
this.removeSelectionAttributes(selectionPath);
|
||||
});
|
||||
this.addSelectionAttributes(selectable);
|
||||
};
|
||||
|
||||
Selection.prototype.removeSelectionAttributes = function (selectionPath, keepParentStyle) {
|
||||
if (selectionPath[0] && selectionPath[0].element) {
|
||||
selectionPath[0].element.removeAttribute('s-selected');
|
||||
if (this.selected[1] && this.selected[1].element) {
|
||||
this.selected[1].element.removeAttribute('s-selected-parent');
|
||||
}
|
||||
|
||||
if (selectionPath[1] && selectionPath[1].element && !keepParentStyle) {
|
||||
selectionPath[1].element.removeAttribute('s-selected-parent');
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Adds selection attributes to the selected element and its parent.
|
||||
* @private
|
||||
*/
|
||||
Selection.prototype.addSelectionAttributes = function (selectable) {
|
||||
if (selectable[0] && selectable[0].element) {
|
||||
selectable[0].element.setAttribute('s-selected', "");
|
||||
}
|
||||
@@ -143,36 +69,16 @@ define(
|
||||
if (selectable[1] && selectable[1].element) {
|
||||
selectable[1].element.setAttribute('s-selected-parent', "");
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
Selection.prototype.parentSupportsMultiSelect = function (selectable) {
|
||||
return selectable[1] && selectable[1].context.supportsMultiSelect;
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
Selection.prototype.selectionContainsParent = function (selectable) {
|
||||
return this.selected.some(selectionPath => _.isEqual(selectionPath[0], selectable[1]));
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
Selection.prototype.isPeer = function (selectable) {
|
||||
return this.selected.some(selectionPath => _.isEqual(selectionPath[1], selectable[1]));
|
||||
this.selected = selectable;
|
||||
this.emit('change', this.selected);
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
Selection.prototype.capture = function (selectable) {
|
||||
let capturingContainsSelectable = this.capturing && this.capturing.includes(selectable);
|
||||
|
||||
if (!this.capturing || capturingContainsSelectable) {
|
||||
if (!this.capturing) {
|
||||
this.capturing = [];
|
||||
}
|
||||
|
||||
@@ -182,14 +88,13 @@ define(
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
Selection.prototype.selectCapture = function (selectable, event) {
|
||||
Selection.prototype.selectCapture = function (selectable) {
|
||||
if (!this.capturing) {
|
||||
return;
|
||||
}
|
||||
|
||||
let reversedCapturing = this.capturing.reverse();
|
||||
this.select(this.capturing.reverse());
|
||||
delete this.capturing;
|
||||
this.select(reversedCapturing, event.shiftKey);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -207,7 +112,7 @@ define(
|
||||
* @public
|
||||
*/
|
||||
Selection.prototype.selectable = function (element, context, select) {
|
||||
let selectable = {
|
||||
var selectable = {
|
||||
context: context,
|
||||
element: element
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user