Compare commits
1 Commits
misc-ui-4b
...
layout-imp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
50e994f982 |
@@ -37,25 +37,25 @@ define([
|
|||||||
},
|
},
|
||||||
LIMITS = {
|
LIMITS = {
|
||||||
rh: {
|
rh: {
|
||||||
cssClass: "is-limit--upr is-limit--red",
|
cssClass: "s-limit-upr s-limit-red",
|
||||||
low: RED,
|
low: RED,
|
||||||
high: Number.POSITIVE_INFINITY,
|
high: Number.POSITIVE_INFINITY,
|
||||||
name: "Red High"
|
name: "Red High"
|
||||||
},
|
},
|
||||||
rl: {
|
rl: {
|
||||||
cssClass: "is-limit--lwr is-limit--red",
|
cssClass: "s-limit-lwr s-limit-red",
|
||||||
high: -RED,
|
high: -RED,
|
||||||
low: Number.NEGATIVE_INFINITY,
|
low: Number.NEGATIVE_INFINITY,
|
||||||
name: "Red Low"
|
name: "Red Low"
|
||||||
},
|
},
|
||||||
yh: {
|
yh: {
|
||||||
cssClass: "is-limit--upr is-limit--yellow",
|
cssClass: "s-limit-upr s-limit-yellow",
|
||||||
low: YELLOW,
|
low: YELLOW,
|
||||||
high: RED,
|
high: RED,
|
||||||
name: "Yellow High"
|
name: "Yellow High"
|
||||||
},
|
},
|
||||||
yl: {
|
yl: {
|
||||||
cssClass: "is-limit--lwr is-limit--yellow",
|
cssClass: "s-limit-lwr s-limit-yellow",
|
||||||
low: -RED,
|
low: -RED,
|
||||||
high: -YELLOW,
|
high: -YELLOW,
|
||||||
name: "Yellow Low"
|
name: "Yellow Low"
|
||||||
|
|||||||
@@ -79,7 +79,6 @@
|
|||||||
openmct.install(openmct.plugins.FolderView());
|
openmct.install(openmct.plugins.FolderView());
|
||||||
openmct.install(openmct.plugins.Tabs());
|
openmct.install(openmct.plugins.Tabs());
|
||||||
openmct.install(openmct.plugins.FlexibleLayout());
|
openmct.install(openmct.plugins.FlexibleLayout());
|
||||||
openmct.install(openmct.plugins.LADTable());
|
|
||||||
openmct.time.clock('local', {start: -THIRTY_MINUTES, end: 0});
|
openmct.time.clock('local', {start: -THIRTY_MINUTES, end: 0});
|
||||||
openmct.time.timeSystem('utc');
|
openmct.time.timeSystem('utc');
|
||||||
openmct.start();
|
openmct.start();
|
||||||
|
|||||||
@@ -58,6 +58,7 @@
|
|||||||
"printj": "^1.1.0",
|
"printj": "^1.1.0",
|
||||||
"raw-loader": "^0.5.1",
|
"raw-loader": "^0.5.1",
|
||||||
"request": "^2.69.0",
|
"request": "^2.69.0",
|
||||||
|
"screenfull": "^3.3.2",
|
||||||
"split": "^1.0.0",
|
"split": "^1.0.0",
|
||||||
"style-loader": "^0.21.0",
|
"style-loader": "^0.21.0",
|
||||||
"v8-compile-cache": "^1.1.0",
|
"v8-compile-cache": "^1.1.0",
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ define([
|
|||||||
"./src/navigation/NavigateAction",
|
"./src/navigation/NavigateAction",
|
||||||
"./src/navigation/OrphanNavigationHandler",
|
"./src/navigation/OrphanNavigationHandler",
|
||||||
"./src/windowing/NewTabAction",
|
"./src/windowing/NewTabAction",
|
||||||
|
"./src/windowing/FullscreenAction",
|
||||||
"./src/windowing/WindowTitler",
|
"./src/windowing/WindowTitler",
|
||||||
"./res/templates/browse.html",
|
"./res/templates/browse.html",
|
||||||
"./res/templates/browse-object.html",
|
"./res/templates/browse-object.html",
|
||||||
@@ -52,6 +53,7 @@ define([
|
|||||||
NavigateAction,
|
NavigateAction,
|
||||||
OrphanNavigationHandler,
|
OrphanNavigationHandler,
|
||||||
NewTabAction,
|
NewTabAction,
|
||||||
|
FullscreenAction,
|
||||||
WindowTitler,
|
WindowTitler,
|
||||||
browseTemplate,
|
browseTemplate,
|
||||||
browseObjectTemplate,
|
browseObjectTemplate,
|
||||||
@@ -223,6 +225,13 @@ define([
|
|||||||
"group": "windowing",
|
"group": "windowing",
|
||||||
"cssClass": "icon-new-window",
|
"cssClass": "icon-new-window",
|
||||||
"priority": "preferred"
|
"priority": "preferred"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "fullscreen",
|
||||||
|
"implementation": FullscreenAction,
|
||||||
|
"category": "view-control",
|
||||||
|
"group": "windowing",
|
||||||
|
"priority": "default"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"runs": [
|
"runs": [
|
||||||
@@ -256,6 +265,18 @@ define([
|
|||||||
key: "inspectorRegion",
|
key: "inspectorRegion",
|
||||||
template: inspectorRegionTemplate
|
template: inspectorRegionTemplate
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"licenses": [
|
||||||
|
{
|
||||||
|
"name": "screenfull.js",
|
||||||
|
"version": "1.2.0",
|
||||||
|
"description": "Wrapper for cross-browser usage of fullscreen API",
|
||||||
|
"author": "Sindre Sorhus",
|
||||||
|
"website": "https://github.com/sindresorhus/screenfull.js/",
|
||||||
|
"copyright": "Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)",
|
||||||
|
"license": "license-mit",
|
||||||
|
"link": "https://github.com/sindresorhus/screenfull.js/blob/gh-pages/license"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
64
platform/commonUI/browse/src/windowing/FullscreenAction.js
Normal file
64
platform/commonUI/browse/src/windowing/FullscreenAction.js
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module defining FullscreenAction. Created by vwoeltje on 11/18/14.
|
||||||
|
*/
|
||||||
|
define(
|
||||||
|
["screenfull"],
|
||||||
|
function (screenfull) {
|
||||||
|
|
||||||
|
var ENTER_FULLSCREEN = "Enter full screen mode",
|
||||||
|
EXIT_FULLSCREEN = "Exit full screen mode";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The fullscreen action toggles between fullscreen display
|
||||||
|
* and regular in-window display.
|
||||||
|
* @memberof platform/commonUI/browse
|
||||||
|
* @constructor
|
||||||
|
* @implements {Action}
|
||||||
|
*/
|
||||||
|
function FullscreenAction(context) {
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
FullscreenAction.prototype.perform = function () {
|
||||||
|
screenfull.toggle();
|
||||||
|
};
|
||||||
|
|
||||||
|
FullscreenAction.prototype.getMetadata = function () {
|
||||||
|
// We override getMetadata, because the icon cssClass and
|
||||||
|
// description need to be determined at run-time
|
||||||
|
// based on whether or not we are currently
|
||||||
|
// full screen.
|
||||||
|
var metadata = Object.create(FullscreenAction);
|
||||||
|
metadata.cssClass = screenfull.isFullscreen ? "icon-fullscreen-expand" : "icon-fullscreen-collapse";
|
||||||
|
metadata.description = screenfull.isFullscreen ?
|
||||||
|
EXIT_FULLSCREEN : ENTER_FULLSCREEN;
|
||||||
|
metadata.group = "windowing";
|
||||||
|
metadata.context = this.context;
|
||||||
|
return metadata;
|
||||||
|
};
|
||||||
|
|
||||||
|
return FullscreenAction;
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MCTRepresentationSpec. Created by vwoeltje on 11/6/14.
|
||||||
|
*/
|
||||||
|
define(
|
||||||
|
["../../src/windowing/FullscreenAction", "screenfull"],
|
||||||
|
function (FullscreenAction, screenfull) {
|
||||||
|
|
||||||
|
describe("The fullscreen action", function () {
|
||||||
|
var action,
|
||||||
|
oldToggle;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
// Screenfull is not shimmed or injected, so
|
||||||
|
// we need to spy on it in the global scope.
|
||||||
|
oldToggle = screenfull.toggle;
|
||||||
|
|
||||||
|
screenfull.toggle = jasmine.createSpy("toggle");
|
||||||
|
|
||||||
|
action = new FullscreenAction({});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function () {
|
||||||
|
screenfull.toggle = oldToggle;
|
||||||
|
});
|
||||||
|
|
||||||
|
it("toggles fullscreen mode when performed", function () {
|
||||||
|
action.perform();
|
||||||
|
expect(screenfull.toggle).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("provides displayable metadata", function () {
|
||||||
|
expect(action.getMetadata().cssClass).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -95,10 +95,7 @@ define(
|
|||||||
|
|
||||||
// Create the overlay element and add it to the document's body
|
// Create the overlay element and add it to the document's body
|
||||||
element = this.$compile(TEMPLATE)(scope);
|
element = this.$compile(TEMPLATE)(scope);
|
||||||
|
this.findBody().prepend(element);
|
||||||
// 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 {
|
return {
|
||||||
dismiss: dismiss
|
dismiss: dismiss
|
||||||
|
|||||||
@@ -32,7 +32,11 @@ define([
|
|||||||
"./src/actions/SaveAndStopEditingAction",
|
"./src/actions/SaveAndStopEditingAction",
|
||||||
"./src/actions/SaveAsAction",
|
"./src/actions/SaveAsAction",
|
||||||
"./src/actions/CancelAction",
|
"./src/actions/CancelAction",
|
||||||
|
"./src/policies/EditActionPolicy",
|
||||||
"./src/policies/EditPersistableObjectsPolicy",
|
"./src/policies/EditPersistableObjectsPolicy",
|
||||||
|
"./src/policies/EditableLinkPolicy",
|
||||||
|
"./src/policies/EditableMovePolicy",
|
||||||
|
"./src/policies/EditContextualActionPolicy",
|
||||||
"./src/representers/EditRepresenter",
|
"./src/representers/EditRepresenter",
|
||||||
"./src/capabilities/EditorCapability",
|
"./src/capabilities/EditorCapability",
|
||||||
"./src/capabilities/TransactionCapabilityDecorator",
|
"./src/capabilities/TransactionCapabilityDecorator",
|
||||||
@@ -63,7 +67,11 @@ define([
|
|||||||
SaveAndStopEditingAction,
|
SaveAndStopEditingAction,
|
||||||
SaveAsAction,
|
SaveAsAction,
|
||||||
CancelAction,
|
CancelAction,
|
||||||
|
EditActionPolicy,
|
||||||
EditPersistableObjectsPolicy,
|
EditPersistableObjectsPolicy,
|
||||||
|
EditableLinkPolicy,
|
||||||
|
EditableMovePolicy,
|
||||||
|
EditContextualActionPolicy,
|
||||||
EditRepresenter,
|
EditRepresenter,
|
||||||
EditorCapability,
|
EditorCapability,
|
||||||
TransactionCapabilityDecorator,
|
TransactionCapabilityDecorator,
|
||||||
@@ -166,7 +174,7 @@ define([
|
|||||||
"name": "Remove",
|
"name": "Remove",
|
||||||
"description": "Remove this object from its containing object.",
|
"description": "Remove this object from its containing object.",
|
||||||
"depends": [
|
"depends": [
|
||||||
"openmct",
|
"dialogService",
|
||||||
"navigationService"
|
"navigationService"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -223,11 +231,28 @@ define([
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"policies": [
|
"policies": [
|
||||||
|
{
|
||||||
|
"category": "action",
|
||||||
|
"implementation": EditActionPolicy
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"category": "action",
|
"category": "action",
|
||||||
"implementation": EditPersistableObjectsPolicy,
|
"implementation": EditPersistableObjectsPolicy,
|
||||||
"depends": ["openmct"]
|
"depends": ["openmct"]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"category": "action",
|
||||||
|
"implementation": EditContextualActionPolicy,
|
||||||
|
"depends": ["navigationService", "editModeBlacklist", "nonEditContextBlacklist"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "action",
|
||||||
|
"implementation": EditableMovePolicy
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "action",
|
||||||
|
"implementation": EditableLinkPolicy
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"implementation": CreationPolicy,
|
"implementation": CreationPolicy,
|
||||||
"category": "creation"
|
"category": "creation"
|
||||||
@@ -324,6 +349,16 @@ define([
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"constants": [
|
||||||
|
{
|
||||||
|
"key": "editModeBlacklist",
|
||||||
|
"value": ["copy", "follow", "link", "locate"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "nonEditContextBlacklist",
|
||||||
|
"value": ["copy", "follow", "properties", "move", "link", "remove", "locate"]
|
||||||
|
}
|
||||||
|
],
|
||||||
"capabilities": [
|
"capabilities": [
|
||||||
{
|
{
|
||||||
"key": "editor",
|
"key": "editor",
|
||||||
|
|||||||
@@ -42,9 +42,9 @@ define([
|
|||||||
* @constructor
|
* @constructor
|
||||||
* @implements {Action}
|
* @implements {Action}
|
||||||
*/
|
*/
|
||||||
function RemoveAction(openmct, navigationService, context) {
|
function RemoveAction(dialogService, navigationService, context) {
|
||||||
this.domainObject = (context || {}).domainObject;
|
this.domainObject = (context || {}).domainObject;
|
||||||
this.openmct = openmct;
|
this.dialogService = dialogService;
|
||||||
this.navigationService = navigationService;
|
this.navigationService = navigationService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,6 +53,7 @@ define([
|
|||||||
*/
|
*/
|
||||||
RemoveAction.prototype.perform = function () {
|
RemoveAction.prototype.perform = function () {
|
||||||
var dialog,
|
var dialog,
|
||||||
|
dialogService = this.dialogService,
|
||||||
domainObject = this.domainObject,
|
domainObject = this.domainObject,
|
||||||
navigationService = this.navigationService;
|
navigationService = this.navigationService;
|
||||||
/*
|
/*
|
||||||
@@ -103,13 +104,13 @@ define([
|
|||||||
* capability. Based on object's location and selected object's location
|
* capability. Based on object's location and selected object's location
|
||||||
* user may be navigated to existing parent object
|
* user may be navigated to existing parent object
|
||||||
*/
|
*/
|
||||||
function removeFromContext() {
|
function removeFromContext(object) {
|
||||||
var contextCapability = domainObject.getCapability('context'),
|
var contextCapability = object.getCapability('context'),
|
||||||
parent = contextCapability.getParent();
|
parent = contextCapability.getParent();
|
||||||
|
|
||||||
// If currently within path of removed object(s),
|
// If currently within path of removed object(s),
|
||||||
// navigates to existing object up tree
|
// navigates to existing object up tree
|
||||||
checkObjectNavigation(domainObject, parent);
|
checkObjectNavigation(object, parent);
|
||||||
|
|
||||||
return parent.useCapability('mutation', doMutate);
|
return parent.useCapability('mutation', doMutate);
|
||||||
}
|
}
|
||||||
@@ -118,7 +119,7 @@ define([
|
|||||||
* Pass in the function to remove the domain object so it can be
|
* Pass in the function to remove the domain object so it can be
|
||||||
* associated with an 'OK' button press
|
* associated with an 'OK' button press
|
||||||
*/
|
*/
|
||||||
dialog = new RemoveDialog(this.openmct, domainObject, removeFromContext);
|
dialog = new RemoveDialog(dialogService, domainObject, removeFromContext);
|
||||||
dialog.show();
|
dialog.show();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -36,8 +36,8 @@ define([], function () {
|
|||||||
* @memberof platform/commonUI/edit
|
* @memberof platform/commonUI/edit
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function RemoveDialog(openmct, domainObject, removeCallback) {
|
function RemoveDialog(dialogService, domainObject, removeCallback) {
|
||||||
this.openmct = openmct;
|
this.dialogService = dialogService;
|
||||||
this.domainObject = domainObject;
|
this.domainObject = domainObject;
|
||||||
this.removeCallback = removeCallback;
|
this.removeCallback = removeCallback;
|
||||||
}
|
}
|
||||||
@@ -46,26 +46,31 @@ define([], function () {
|
|||||||
* Display a dialog to confirm the removal of a domain object.
|
* Display a dialog to confirm the removal of a domain object.
|
||||||
*/
|
*/
|
||||||
RemoveDialog.prototype.show = function () {
|
RemoveDialog.prototype.show = function () {
|
||||||
let dialog = this.openmct.overlays.dialog({
|
var dialog,
|
||||||
title: 'Remove ' + this.domainObject.getModel().name,
|
domainObject = this.domainObject,
|
||||||
iconClass: 'alert',
|
removeCallback = this.removeCallback,
|
||||||
message: 'Warning! This action will permanently remove this object. Are you sure you want to continue?',
|
model = {
|
||||||
buttons: [
|
title: 'Remove ' + domainObject.getModel().name,
|
||||||
{
|
actionText: 'Warning! This action will permanently remove this object. Are you sure you want to continue?',
|
||||||
|
severity: 'alert',
|
||||||
|
primaryOption: {
|
||||||
label: 'OK',
|
label: 'OK',
|
||||||
callback: () => {
|
callback: function () {
|
||||||
this.removeCallback();
|
removeCallback(domainObject);
|
||||||
dialog.dismiss();
|
dialog.dismiss();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
options: [
|
||||||
label: 'Cancel',
|
{
|
||||||
callback: () => {
|
label: 'Cancel',
|
||||||
dialog.dismiss();
|
callback: function () {
|
||||||
|
dialog.dismiss();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
]
|
||||||
]
|
};
|
||||||
});
|
setTimeout(() => this.removeCallback(domainObject));
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return RemoveDialog;
|
return RemoveDialog;
|
||||||
|
|||||||
111
platform/commonUI/edit/src/policies/EditActionPolicy.js
Normal file
111
platform/commonUI/edit/src/policies/EditActionPolicy.js
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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 () {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Policy controlling when the `edit` and/or `properties` actions
|
||||||
|
* can appear as applicable actions of the `view-control` category
|
||||||
|
* (shown as buttons in the top-right of browse mode.)
|
||||||
|
* @memberof platform/commonUI/edit
|
||||||
|
* @constructor
|
||||||
|
* @implements {Policy.<Action, ActionContext>}
|
||||||
|
*/
|
||||||
|
function EditActionPolicy(policyService) {
|
||||||
|
this.policyService = policyService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a count of views which are not flagged as non-editable.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
EditActionPolicy.prototype.countEditableViews = function (context) {
|
||||||
|
var domainObject = context.domainObject,
|
||||||
|
count = 0,
|
||||||
|
type, views;
|
||||||
|
|
||||||
|
if (!domainObject) {
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
type = domainObject.getCapability('type');
|
||||||
|
views = domainObject.useCapability('view');
|
||||||
|
|
||||||
|
|
||||||
|
// A view is editable unless explicitly flagged as not
|
||||||
|
(views || []).forEach(function (view) {
|
||||||
|
if (isEditable(view) ||
|
||||||
|
(view.key === 'plot' && type.getKey() === 'telemetry.panel') ||
|
||||||
|
(view.key === 'table' && type.getKey() === 'table') ||
|
||||||
|
(view.key === 'rt-table' && type.getKey() === 'rttable')
|
||||||
|
) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function isEditable(view) {
|
||||||
|
if (typeof view.editable === Function) {
|
||||||
|
return view.editable(domainObject.useCapability('adapter'));
|
||||||
|
} else {
|
||||||
|
return view.editable === true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the domain object is currently being edited. If
|
||||||
|
* so, the edit action is not applicable.
|
||||||
|
* @param context
|
||||||
|
* @returns {*|boolean}
|
||||||
|
*/
|
||||||
|
function isEditing(context) {
|
||||||
|
var domainObject = (context || {}).domainObject;
|
||||||
|
return domainObject &&
|
||||||
|
domainObject.hasCapability('editor') &&
|
||||||
|
domainObject.getCapability('editor').isEditContextRoot();
|
||||||
|
}
|
||||||
|
|
||||||
|
EditActionPolicy.prototype.allow = function (action, context) {
|
||||||
|
var key = action.getMetadata().key,
|
||||||
|
category = (context || {}).category;
|
||||||
|
|
||||||
|
// Restrict 'edit' to cases where there are editable
|
||||||
|
// views (similarly, restrict 'properties' to when
|
||||||
|
// the converse is true), and where the domain object is not
|
||||||
|
// already being edited.
|
||||||
|
if (key === 'edit') {
|
||||||
|
return this.countEditableViews(context) > 0 && !isEditing(context);
|
||||||
|
} else if (key === 'properties' && category === 'view-control') {
|
||||||
|
return this.countEditableViews(context) < 1 && !isEditing(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Like all policies, allow by default.
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
return EditActionPolicy;
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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 () {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Policy controlling whether the context menu is visible when
|
||||||
|
* objects are being edited
|
||||||
|
* @param navigationService
|
||||||
|
* @param editModeBlacklist A blacklist of actions disallowed from
|
||||||
|
* context menu when navigated object is being edited
|
||||||
|
* @param nonEditContextBlacklist A blacklist of actions disallowed
|
||||||
|
* from context menu of non-editable objects, when navigated object
|
||||||
|
* is being edited
|
||||||
|
* @constructor
|
||||||
|
* @param editModeBlacklist A blacklist of actions disallowed from
|
||||||
|
* context menu when navigated object is being edited
|
||||||
|
* @param nonEditContextBlacklist A blacklist of actions disallowed
|
||||||
|
* from context menu of non-editable objects, when navigated object
|
||||||
|
* @implements {Policy.<Action, ActionContext>}
|
||||||
|
*/
|
||||||
|
function EditContextualActionPolicy(navigationService, editModeBlacklist, nonEditContextBlacklist) {
|
||||||
|
this.navigationService = navigationService;
|
||||||
|
|
||||||
|
//The list of objects disallowed on target object when in edit mode
|
||||||
|
this.editModeBlacklist = editModeBlacklist;
|
||||||
|
//The list of objects disallowed on target object that is not in
|
||||||
|
// edit mode (ie. the context menu in the tree on the LHS).
|
||||||
|
this.nonEditContextBlacklist = nonEditContextBlacklist;
|
||||||
|
}
|
||||||
|
|
||||||
|
EditContextualActionPolicy.prototype.allow = function (action, context) {
|
||||||
|
var selectedObject = context.domainObject,
|
||||||
|
navigatedObject = this.navigationService.getNavigation(),
|
||||||
|
actionMetadata = action.getMetadata ? action.getMetadata() : {};
|
||||||
|
|
||||||
|
// FIXME: need to restore support for changing contextual actions
|
||||||
|
// based on edit mode.
|
||||||
|
// if (navigatedObject.hasCapability("editor") && navigatedObject.getCapability("editor").isEditContextRoot()) {
|
||||||
|
// if (selectedObject.hasCapability("editor") && selectedObject.getCapability("editor").inEditContext()) {
|
||||||
|
// return this.editModeBlacklist.indexOf(actionMetadata.key) === -1;
|
||||||
|
// } else {
|
||||||
|
// //Target is in the context menu
|
||||||
|
// return this.nonEditContextBlacklist.indexOf(actionMetadata.key) === -1;
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
return EditContextualActionPolicy;
|
||||||
|
}
|
||||||
|
);
|
||||||
51
platform/commonUI/edit/src/policies/EditableLinkPolicy.js
Normal file
51
platform/commonUI/edit/src/policies/EditableLinkPolicy.js
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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 () {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Policy suppressing links when the linked-to domain object is in
|
||||||
|
* edit mode. Domain objects being edited may not have been persisted,
|
||||||
|
* so creating links to these can result in inconsistent state.
|
||||||
|
*
|
||||||
|
* @memberof platform/commonUI/edit
|
||||||
|
* @constructor
|
||||||
|
* @implements {Policy.<View, DomainObject>}
|
||||||
|
*/
|
||||||
|
function EditableLinkPolicy() {
|
||||||
|
}
|
||||||
|
|
||||||
|
EditableLinkPolicy.prototype.allow = function (action, context) {
|
||||||
|
var key = action.getMetadata().key,
|
||||||
|
object;
|
||||||
|
|
||||||
|
if (key === 'link') {
|
||||||
|
object = context.selectedObject || context.domainObject;
|
||||||
|
return !(object.hasCapability("editor") && object.getCapability("editor").inEditContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Like all policies, allow by default.
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
return EditableLinkPolicy;
|
||||||
|
});
|
||||||
52
platform/commonUI/edit/src/policies/EditableMovePolicy.js
Normal file
52
platform/commonUI/edit/src/policies/EditableMovePolicy.js
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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 () {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Policy suppressing move actions among editable and non-editable
|
||||||
|
* domain objects.
|
||||||
|
* @memberof platform/commonUI/edit
|
||||||
|
* @constructor
|
||||||
|
* @implements {Policy.<View, DomainObject>}
|
||||||
|
*/
|
||||||
|
function EditableMovePolicy() {
|
||||||
|
}
|
||||||
|
|
||||||
|
EditableMovePolicy.prototype.allow = function (action, context) {
|
||||||
|
var domainObject = context.domainObject,
|
||||||
|
selectedObject = context.selectedObject,
|
||||||
|
key = action.getMetadata().key,
|
||||||
|
isDomainObjectEditing = domainObject.hasCapability('editor') &&
|
||||||
|
domainObject.getCapability('editor').inEditContext();
|
||||||
|
|
||||||
|
if (key === 'move' && isDomainObjectEditing) {
|
||||||
|
return !!selectedObject && selectedObject.hasCapability('editor') &&
|
||||||
|
selectedObject.getCapability('editor').inEditContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Like all policies, allow by default.
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
return EditableMovePolicy;
|
||||||
|
});
|
||||||
49
platform/commonUI/edit/src/policies/EditableViewPolicy.js
Normal file
49
platform/commonUI/edit/src/policies/EditableViewPolicy.js
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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 () {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Policy controlling which views should be visible in Edit mode.
|
||||||
|
* @memberof platform/commonUI/edit
|
||||||
|
* @constructor
|
||||||
|
* @implements {Policy.<View, DomainObject>}
|
||||||
|
*/
|
||||||
|
function EditableViewPolicy() {
|
||||||
|
}
|
||||||
|
|
||||||
|
EditableViewPolicy.prototype.allow = function (view, domainObject) {
|
||||||
|
// If a view is flagged as non-editable, only allow it
|
||||||
|
// while we're not in Edit mode.
|
||||||
|
if ((view || {}).editable === false) {
|
||||||
|
return !(domainObject.hasCapability('editor') && domainObject.getCapability('editor').inEditContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Like all policies, allow by default.
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
return EditableViewPolicy;
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -29,7 +29,7 @@ define(
|
|||||||
actionContext,
|
actionContext,
|
||||||
capabilities,
|
capabilities,
|
||||||
mockContext,
|
mockContext,
|
||||||
mockOverlayAPI,
|
mockDialogService,
|
||||||
mockDomainObject,
|
mockDomainObject,
|
||||||
mockMutation,
|
mockMutation,
|
||||||
mockNavigationService,
|
mockNavigationService,
|
||||||
@@ -68,9 +68,9 @@ define(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
mockOverlayAPI = jasmine.createSpyObj(
|
mockDialogService = jasmine.createSpyObj(
|
||||||
"overlayAPI",
|
"dialogService",
|
||||||
["dialog"]
|
["showBlockingMessage"]
|
||||||
);
|
);
|
||||||
|
|
||||||
mockNavigationService = jasmine.createSpyObj(
|
mockNavigationService = jasmine.createSpyObj(
|
||||||
@@ -96,7 +96,7 @@ define(
|
|||||||
|
|
||||||
actionContext = { domainObject: mockDomainObject };
|
actionContext = { domainObject: mockDomainObject };
|
||||||
|
|
||||||
action = new RemoveAction({overlays: mockOverlayAPI}, mockNavigationService, actionContext);
|
action = new RemoveAction(mockDialogService, mockNavigationService, actionContext);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("only applies to objects with parents", function () {
|
it("only applies to objects with parents", function () {
|
||||||
@@ -118,7 +118,7 @@ define(
|
|||||||
|
|
||||||
action.perform();
|
action.perform();
|
||||||
|
|
||||||
expect(mockOverlayAPI.dialog).toHaveBeenCalled();
|
expect(mockDialogService.showBlockingMessage).toHaveBeenCalled();
|
||||||
|
|
||||||
// Also check that no mutation happens at this point
|
// Also check that no mutation happens at this point
|
||||||
expect(mockParent.useCapability).not.toHaveBeenCalledWith("mutation", jasmine.any(Function));
|
expect(mockParent.useCapability).not.toHaveBeenCalledWith("mutation", jasmine.any(Function));
|
||||||
@@ -158,13 +158,13 @@ define(
|
|||||||
mockGrandchildContext = jasmine.createSpyObj("context", ["getParent"]);
|
mockGrandchildContext = jasmine.createSpyObj("context", ["getParent"]);
|
||||||
mockRootContext = jasmine.createSpyObj("context", ["getParent"]);
|
mockRootContext = jasmine.createSpyObj("context", ["getParent"]);
|
||||||
|
|
||||||
mockOverlayAPI.dialog.and.returnValue(mockDialogHandle);
|
mockDialogService.showBlockingMessage.and.returnValue(mockDialogHandle);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("mutates the parent when performed", function () {
|
it("mutates the parent when performed", function () {
|
||||||
action.perform();
|
action.perform();
|
||||||
mockOverlayAPI.dialog.calls.mostRecent().args[0]
|
mockDialogService.showBlockingMessage.calls.mostRecent().args[0]
|
||||||
.buttons[0].callback();
|
.primaryOption.callback();
|
||||||
|
|
||||||
expect(mockMutation.invoke)
|
expect(mockMutation.invoke)
|
||||||
.toHaveBeenCalledWith(jasmine.any(Function));
|
.toHaveBeenCalledWith(jasmine.any(Function));
|
||||||
@@ -174,8 +174,8 @@ define(
|
|||||||
var mutator, result;
|
var mutator, result;
|
||||||
|
|
||||||
action.perform();
|
action.perform();
|
||||||
mockOverlayAPI.dialog.calls.mostRecent().args[0]
|
mockDialogService.showBlockingMessage.calls.mostRecent().args[0]
|
||||||
.buttons[0].callback();
|
.primaryOption.callback();
|
||||||
|
|
||||||
mutator = mockMutation.invoke.calls.mostRecent().args[0];
|
mutator = mockMutation.invoke.calls.mostRecent().args[0];
|
||||||
result = mutator(model);
|
result = mutator(model);
|
||||||
@@ -212,8 +212,8 @@ define(
|
|||||||
mockType.hasFeature.and.returnValue(true);
|
mockType.hasFeature.and.returnValue(true);
|
||||||
|
|
||||||
action.perform();
|
action.perform();
|
||||||
mockOverlayAPI.dialog.calls.mostRecent().args[0]
|
mockDialogService.showBlockingMessage.calls.mostRecent().args[0]
|
||||||
.buttons[0].callback();
|
.primaryOption.callback();
|
||||||
|
|
||||||
// Expects navigation to parent of domainObject (removed object)
|
// Expects navigation to parent of domainObject (removed object)
|
||||||
expect(mockNavigationService.setNavigation).toHaveBeenCalledWith(mockParent);
|
expect(mockNavigationService.setNavigation).toHaveBeenCalledWith(mockParent);
|
||||||
@@ -242,8 +242,8 @@ define(
|
|||||||
mockType.hasFeature.and.returnValue(true);
|
mockType.hasFeature.and.returnValue(true);
|
||||||
|
|
||||||
action.perform();
|
action.perform();
|
||||||
mockOverlayAPI.dialog.calls.mostRecent().args[0]
|
mockDialogService.showBlockingMessage.calls.mostRecent().args[0]
|
||||||
.buttons[0].callback();
|
.primaryOption.callback();
|
||||||
|
|
||||||
// Expects no navigation to occur
|
// Expects no navigation to occur
|
||||||
expect(mockNavigationService.setNavigation).not.toHaveBeenCalled();
|
expect(mockNavigationService.setNavigation).not.toHaveBeenCalled();
|
||||||
|
|||||||
138
platform/commonUI/edit/test/policies/EditActionPolicySpec.js
Normal file
138
platform/commonUI/edit/test/policies/EditActionPolicySpec.js
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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/policies/EditActionPolicy"],
|
||||||
|
function (EditActionPolicy) {
|
||||||
|
|
||||||
|
describe("The Edit action policy", function () {
|
||||||
|
var editableView,
|
||||||
|
nonEditableView,
|
||||||
|
testViews,
|
||||||
|
testContext,
|
||||||
|
mockDomainObject,
|
||||||
|
mockEditAction,
|
||||||
|
mockPropertiesAction,
|
||||||
|
mockTypeCapability,
|
||||||
|
mockEditorCapability,
|
||||||
|
capabilities,
|
||||||
|
plotView,
|
||||||
|
policy;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
mockDomainObject = jasmine.createSpyObj(
|
||||||
|
'domainObject',
|
||||||
|
[
|
||||||
|
'useCapability',
|
||||||
|
'hasCapability',
|
||||||
|
'getCapability'
|
||||||
|
]
|
||||||
|
);
|
||||||
|
mockEditorCapability = jasmine.createSpyObj('editorCapability', ['isEditContextRoot']);
|
||||||
|
mockTypeCapability = jasmine.createSpyObj('type', ['getKey']);
|
||||||
|
capabilities = {
|
||||||
|
'editor': mockEditorCapability,
|
||||||
|
'type': mockTypeCapability
|
||||||
|
};
|
||||||
|
|
||||||
|
mockEditAction = jasmine.createSpyObj('edit', ['getMetadata']);
|
||||||
|
mockPropertiesAction = jasmine.createSpyObj('edit', ['getMetadata']);
|
||||||
|
|
||||||
|
mockDomainObject.getCapability.and.callFake(function (capability) {
|
||||||
|
return capabilities[capability];
|
||||||
|
});
|
||||||
|
mockDomainObject.hasCapability.and.callFake(function (capability) {
|
||||||
|
return !!capabilities[capability];
|
||||||
|
});
|
||||||
|
|
||||||
|
editableView = { editable: true };
|
||||||
|
nonEditableView = { editable: false };
|
||||||
|
plotView = { key: "plot", editable: false };
|
||||||
|
testViews = [];
|
||||||
|
|
||||||
|
mockDomainObject.useCapability.and.callFake(function (c) {
|
||||||
|
// Provide test views, only for the view capability
|
||||||
|
return c === 'view' && testViews;
|
||||||
|
});
|
||||||
|
|
||||||
|
mockEditAction.getMetadata.and.returnValue({ key: 'edit' });
|
||||||
|
mockPropertiesAction.getMetadata.and.returnValue({ key: 'properties' });
|
||||||
|
|
||||||
|
testContext = {
|
||||||
|
domainObject: mockDomainObject,
|
||||||
|
category: 'view-control'
|
||||||
|
};
|
||||||
|
|
||||||
|
policy = new EditActionPolicy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("allows the edit action when there are editable views", function () {
|
||||||
|
testViews = [editableView];
|
||||||
|
expect(policy.allow(mockEditAction, testContext)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("allows the edit properties action when there are no editable views", function () {
|
||||||
|
testViews = [nonEditableView, nonEditableView];
|
||||||
|
expect(policy.allow(mockPropertiesAction, testContext)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("disallows the edit action when there are no editable views", function () {
|
||||||
|
testViews = [nonEditableView, nonEditableView];
|
||||||
|
expect(policy.allow(mockEditAction, testContext)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("disallows the edit properties action when there are" +
|
||||||
|
" editable views", function () {
|
||||||
|
testViews = [editableView];
|
||||||
|
expect(policy.allow(mockPropertiesAction, testContext)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("disallows the edit action when object is already being" +
|
||||||
|
" edited", function () {
|
||||||
|
testViews = [editableView];
|
||||||
|
mockEditorCapability.isEditContextRoot.and.returnValue(true);
|
||||||
|
expect(policy.allow(mockEditAction, testContext)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("allows editing of panels in plot view", function () {
|
||||||
|
testViews = [plotView];
|
||||||
|
mockTypeCapability.getKey.and.returnValue('telemetry.panel');
|
||||||
|
|
||||||
|
expect(policy.allow(mockEditAction, testContext)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("disallows editing of plot view when object not a panel type", function () {
|
||||||
|
testViews = [plotView];
|
||||||
|
mockTypeCapability.getKey.and.returnValue('something.else');
|
||||||
|
|
||||||
|
expect(policy.allow(mockEditAction, testContext)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it("allows the edit properties outside of the 'view-control' category", function () {
|
||||||
|
testViews = [nonEditableView];
|
||||||
|
testContext.category = "something-else";
|
||||||
|
expect(policy.allow(mockPropertiesAction, testContext)).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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.
|
||||||
|
*****************************************************************************/
|
||||||
|
/*global describe,it,expect,beforeEach,jasmine*/
|
||||||
|
|
||||||
|
define(
|
||||||
|
["../../src/policies/EditContextualActionPolicy"],
|
||||||
|
function (EditContextualActionPolicy) {
|
||||||
|
|
||||||
|
describe("The Edit contextual action policy", function () {
|
||||||
|
var policy,
|
||||||
|
navigationService,
|
||||||
|
mockAction,
|
||||||
|
context,
|
||||||
|
navigatedObject,
|
||||||
|
mockDomainObject,
|
||||||
|
mockEditorCapability,
|
||||||
|
metadata,
|
||||||
|
editModeBlacklist = ["copy", "follow", "window", "link", "locate"],
|
||||||
|
nonEditContextBlacklist = ["copy", "follow", "properties", "move", "link", "remove", "locate"];
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
mockEditorCapability = jasmine.createSpyObj("editorCapability", ["isEditContextRoot", "inEditContext"]);
|
||||||
|
|
||||||
|
navigatedObject = jasmine.createSpyObj("navigatedObject", ["hasCapability", "getCapability"]);
|
||||||
|
navigatedObject.getCapability.and.returnValue(mockEditorCapability);
|
||||||
|
navigatedObject.hasCapability.and.returnValue(false);
|
||||||
|
|
||||||
|
|
||||||
|
mockDomainObject = jasmine.createSpyObj("domainObject", ["hasCapability", "getCapability"]);
|
||||||
|
mockDomainObject.hasCapability.and.returnValue(false);
|
||||||
|
mockDomainObject.getCapability.and.returnValue(mockEditorCapability);
|
||||||
|
|
||||||
|
navigationService = jasmine.createSpyObj("navigationService", ["getNavigation"]);
|
||||||
|
navigationService.getNavigation.and.returnValue(navigatedObject);
|
||||||
|
|
||||||
|
metadata = {key: "move"};
|
||||||
|
mockAction = jasmine.createSpyObj("action", ["getMetadata"]);
|
||||||
|
mockAction.getMetadata.and.returnValue(metadata);
|
||||||
|
|
||||||
|
context = {domainObject: mockDomainObject};
|
||||||
|
|
||||||
|
policy = new EditContextualActionPolicy(navigationService, editModeBlacklist, nonEditContextBlacklist);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Allows all actions when navigated object not in edit mode', function () {
|
||||||
|
expect(policy.allow(mockAction, context)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Allows "window" action when navigated object in edit mode,' +
|
||||||
|
' but selected object not in edit mode ', function () {
|
||||||
|
navigatedObject.hasCapability.and.returnValue(true);
|
||||||
|
mockEditorCapability.isEditContextRoot.and.returnValue(true);
|
||||||
|
metadata.key = "window";
|
||||||
|
expect(policy.allow(mockAction, context)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Allows "remove" action when navigated object in edit mode,' +
|
||||||
|
' and selected object not editable, but its parent is.',
|
||||||
|
function () {
|
||||||
|
var mockParent = jasmine.createSpyObj("parentObject", ["hasCapability"]),
|
||||||
|
mockContextCapability = jasmine.createSpyObj("contextCapability", ["getParent"]);
|
||||||
|
|
||||||
|
mockParent.hasCapability.and.returnValue(true);
|
||||||
|
mockContextCapability.getParent.and.returnValue(mockParent);
|
||||||
|
navigatedObject.hasCapability.and.returnValue(true);
|
||||||
|
|
||||||
|
mockDomainObject.getCapability.and.returnValue(mockContextCapability);
|
||||||
|
mockDomainObject.hasCapability.and.callFake(function (capability) {
|
||||||
|
switch (capability) {
|
||||||
|
case "editor": return false;
|
||||||
|
case "context": return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
metadata.key = "remove";
|
||||||
|
|
||||||
|
expect(policy.allow(mockAction, context)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Disallows "move" action when navigated object in edit mode,' +
|
||||||
|
' but selected object not in edit mode ', function () {
|
||||||
|
navigatedObject.hasCapability.and.returnValue(true);
|
||||||
|
mockEditorCapability.isEditContextRoot.and.returnValue(true);
|
||||||
|
mockEditorCapability.inEditContext.and.returnValue(false);
|
||||||
|
metadata.key = "move";
|
||||||
|
expect(policy.allow(mockAction, context)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Disallows copy action when navigated object and' +
|
||||||
|
' selected object in edit mode', function () {
|
||||||
|
navigatedObject.hasCapability.and.returnValue(true);
|
||||||
|
mockDomainObject.hasCapability.and.returnValue(true);
|
||||||
|
mockEditorCapability.isEditContextRoot.and.returnValue(true);
|
||||||
|
mockEditorCapability.inEditContext.and.returnValue(true);
|
||||||
|
|
||||||
|
metadata.key = "copy";
|
||||||
|
expect(policy.allow(mockAction, context)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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/policies/EditableViewPolicy"],
|
||||||
|
function (EditableViewPolicy) {
|
||||||
|
|
||||||
|
describe("The editable view policy", function () {
|
||||||
|
var mockDomainObject,
|
||||||
|
testMode,
|
||||||
|
policy;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
testMode = true; // Act as if we're in Edit mode by default
|
||||||
|
mockDomainObject = jasmine.createSpyObj(
|
||||||
|
'domainObject',
|
||||||
|
['hasCapability', 'getCapability']
|
||||||
|
);
|
||||||
|
mockDomainObject.getCapability.and.returnValue({
|
||||||
|
inEditContext: function () {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
mockDomainObject.hasCapability.and.callFake(function (c) {
|
||||||
|
return (c === 'editor') && testMode;
|
||||||
|
});
|
||||||
|
|
||||||
|
policy = new EditableViewPolicy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("disallows views in edit mode that are flagged as non-editable", function () {
|
||||||
|
expect(policy.allow({ editable: false }, mockDomainObject))
|
||||||
|
.toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("allows views in edit mode that are flagged as editable", function () {
|
||||||
|
expect(policy.allow({ editable: true }, mockDomainObject))
|
||||||
|
.toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("allows any view outside of edit mode", function () {
|
||||||
|
var testViews = [
|
||||||
|
{ editable: false },
|
||||||
|
{ editable: true },
|
||||||
|
{ someKey: "some value" }
|
||||||
|
];
|
||||||
|
testMode = false; // Act as if we're not in Edit mode
|
||||||
|
|
||||||
|
testViews.forEach(function (testView) {
|
||||||
|
expect(policy.allow(testView, mockDomainObject)).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("treats views with no defined 'editable' property as editable", function () {
|
||||||
|
expect(policy.allow({ someKey: "some value" }, mockDomainObject))
|
||||||
|
.toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -31,6 +31,7 @@ define([
|
|||||||
"./src/controllers/TreeNodeController",
|
"./src/controllers/TreeNodeController",
|
||||||
"./src/controllers/ActionGroupController",
|
"./src/controllers/ActionGroupController",
|
||||||
"./src/controllers/ToggleController",
|
"./src/controllers/ToggleController",
|
||||||
|
"./src/controllers/ContextMenuController",
|
||||||
"./src/controllers/ClickAwayController",
|
"./src/controllers/ClickAwayController",
|
||||||
"./src/controllers/ViewSwitcherController",
|
"./src/controllers/ViewSwitcherController",
|
||||||
"./src/controllers/GetterSetterController",
|
"./src/controllers/GetterSetterController",
|
||||||
@@ -48,6 +49,8 @@ define([
|
|||||||
"./src/directives/MCTSplitter",
|
"./src/directives/MCTSplitter",
|
||||||
"./src/directives/MCTTree",
|
"./src/directives/MCTTree",
|
||||||
"./src/directives/MCTIndicators",
|
"./src/directives/MCTIndicators",
|
||||||
|
"./src/directives/MCTPreview",
|
||||||
|
"./src/actions/MCTPreviewAction",
|
||||||
"./src/filters/ReverseFilter",
|
"./src/filters/ReverseFilter",
|
||||||
"./res/templates/bottombar.html",
|
"./res/templates/bottombar.html",
|
||||||
"./res/templates/controls/action-button.html",
|
"./res/templates/controls/action-button.html",
|
||||||
@@ -62,11 +65,13 @@ define([
|
|||||||
"./res/templates/tree-node.html",
|
"./res/templates/tree-node.html",
|
||||||
"./res/templates/label.html",
|
"./res/templates/label.html",
|
||||||
"./res/templates/controls/action-group.html",
|
"./res/templates/controls/action-group.html",
|
||||||
|
"./res/templates/menu/context-menu.html",
|
||||||
"./res/templates/controls/switcher.html",
|
"./res/templates/controls/switcher.html",
|
||||||
"./res/templates/object-inspector.html",
|
"./res/templates/object-inspector.html",
|
||||||
"./res/templates/controls/selector.html",
|
"./res/templates/controls/selector.html",
|
||||||
"./res/templates/controls/datetime-picker.html",
|
"./res/templates/controls/datetime-picker.html",
|
||||||
"./res/templates/controls/datetime-field.html",
|
"./res/templates/controls/datetime-field.html",
|
||||||
|
"./res/templates/preview.html",
|
||||||
'legacyRegistry'
|
'legacyRegistry'
|
||||||
], function (
|
], function (
|
||||||
UrlService,
|
UrlService,
|
||||||
@@ -79,6 +84,7 @@ define([
|
|||||||
TreeNodeController,
|
TreeNodeController,
|
||||||
ActionGroupController,
|
ActionGroupController,
|
||||||
ToggleController,
|
ToggleController,
|
||||||
|
ContextMenuController,
|
||||||
ClickAwayController,
|
ClickAwayController,
|
||||||
ViewSwitcherController,
|
ViewSwitcherController,
|
||||||
GetterSetterController,
|
GetterSetterController,
|
||||||
@@ -96,6 +102,8 @@ define([
|
|||||||
MCTSplitter,
|
MCTSplitter,
|
||||||
MCTTree,
|
MCTTree,
|
||||||
MCTIndicators,
|
MCTIndicators,
|
||||||
|
MCTPreview,
|
||||||
|
MCTPreviewAction,
|
||||||
ReverseFilter,
|
ReverseFilter,
|
||||||
bottombarTemplate,
|
bottombarTemplate,
|
||||||
actionButtonTemplate,
|
actionButtonTemplate,
|
||||||
@@ -110,11 +118,13 @@ define([
|
|||||||
treeNodeTemplate,
|
treeNodeTemplate,
|
||||||
labelTemplate,
|
labelTemplate,
|
||||||
actionGroupTemplate,
|
actionGroupTemplate,
|
||||||
|
contextMenuTemplate,
|
||||||
switcherTemplate,
|
switcherTemplate,
|
||||||
objectInspectorTemplate,
|
objectInspectorTemplate,
|
||||||
selectorTemplate,
|
selectorTemplate,
|
||||||
datetimePickerTemplate,
|
datetimePickerTemplate,
|
||||||
datetimeFieldTemplate,
|
datetimeFieldTemplate,
|
||||||
|
previewTemplate,
|
||||||
legacyRegistry
|
legacyRegistry
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@@ -242,6 +252,13 @@ define([
|
|||||||
"key": "ToggleController",
|
"key": "ToggleController",
|
||||||
"implementation": ToggleController
|
"implementation": ToggleController
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"key": "ContextMenuController",
|
||||||
|
"implementation": ContextMenuController,
|
||||||
|
"depends": [
|
||||||
|
"$scope"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"key": "ClickAwayController",
|
"key": "ClickAwayController",
|
||||||
"implementation": ClickAwayController,
|
"implementation": ClickAwayController,
|
||||||
@@ -377,6 +394,31 @@ define([
|
|||||||
"key": "mctIndicators",
|
"key": "mctIndicators",
|
||||||
"implementation": MCTIndicators,
|
"implementation": MCTIndicators,
|
||||||
"depends": ['openmct']
|
"depends": ['openmct']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "mctPreview",
|
||||||
|
"implementation": MCTPreview,
|
||||||
|
"depends": [
|
||||||
|
"$document"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"key": "mct-preview-action",
|
||||||
|
"implementation": MCTPreviewAction,
|
||||||
|
"name": "Preview",
|
||||||
|
"cssClass": "hide-in-t-main-view icon-eye-open",
|
||||||
|
"description": "Preview in large dialog",
|
||||||
|
"category": [
|
||||||
|
"contextual",
|
||||||
|
"view-control"
|
||||||
|
],
|
||||||
|
"depends": [
|
||||||
|
"$compile",
|
||||||
|
"$rootScope"
|
||||||
|
],
|
||||||
|
"priority": "preferred"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"constants": [
|
"constants": [
|
||||||
@@ -475,6 +517,13 @@ define([
|
|||||||
"action"
|
"action"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"key": "context-menu",
|
||||||
|
"template": contextMenuTemplate,
|
||||||
|
"uses": [
|
||||||
|
"action"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"key": "switcher",
|
"key": "switcher",
|
||||||
"template": switcherTemplate,
|
"template": switcherTemplate,
|
||||||
@@ -485,6 +534,10 @@ define([
|
|||||||
{
|
{
|
||||||
"key": "object-inspector",
|
"key": "object-inspector",
|
||||||
"template": objectInspectorTemplate
|
"template": objectInspectorTemplate
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "mct-preview",
|
||||||
|
"template": previewTemplate
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"controls": [
|
"controls": [
|
||||||
|
|||||||
@@ -19,18 +19,15 @@
|
|||||||
this source code distribution or the Licensing information page available
|
this source code distribution or the Licensing information page available
|
||||||
at runtime from the About dialog for additional information.
|
at runtime from the About dialog for additional information.
|
||||||
-->
|
-->
|
||||||
<span ng-controller="SnapshotPreviewController"
|
<div class="menu-element context-menu-wrapper mobile-disable-select" ng-controller="ContextMenuController">
|
||||||
class='form-control shell'>
|
<div class="menu context-menu">
|
||||||
<span class='field control {{structure.cssClass}}'>
|
<ul>
|
||||||
<image
|
<li ng-repeat="menuAction in menuActions"
|
||||||
class="c-ne__embed__snap-thumb"
|
ng-click="menuAction.perform()"
|
||||||
src="{{imageUrl || structure.src}}"
|
title="{{menuAction.getMetadata().description}}"
|
||||||
ng-click="previewImage(imageUrl || structure.src)"
|
class="{{menuAction.getMetadata().cssClass}}">
|
||||||
name="mctControl">
|
{{menuAction.getMetadata().name}}
|
||||||
</image>
|
</li>
|
||||||
<br>
|
</ul>
|
||||||
<a title="Annotate" class="s-button icon-pencil" ng-click="annotateImage(ngModel, field, imageUrl || structure.src)">
|
</div>
|
||||||
<span class="title-label">Annotate</span>
|
</div>
|
||||||
</a>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
45
platform/commonUI/general/res/templates/preview.html
Normal file
45
platform/commonUI/general/res/templates/preview.html
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<!--
|
||||||
|
Open MCT, Copyright (c) 2014-2017, 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.
|
||||||
|
-->
|
||||||
|
<div class="t-frame-inner abs t-object-type-{{ domainObject.getModel().type }}" mct-preview>
|
||||||
|
<div class="abs object-browse-bar l-flex-row">
|
||||||
|
<div class="left flex-elem l-flex-row grows">
|
||||||
|
<mct-representation
|
||||||
|
key="'object-header-frame'"
|
||||||
|
mct-object="domainObject"
|
||||||
|
class="l-flex-row flex-elem object-header grows">
|
||||||
|
</mct-representation>
|
||||||
|
</div>
|
||||||
|
<div class="btn-bar right l-flex-row flex-elem flex-justify-end flex-fixed">
|
||||||
|
<mct-representation
|
||||||
|
key="'switcher'"
|
||||||
|
ng-model="representation"
|
||||||
|
mct-object="domainObject">
|
||||||
|
</mct-representation>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="abs object-holder">
|
||||||
|
<mct-representation
|
||||||
|
key="representation.selected.key"
|
||||||
|
mct-object="representation.selected.key && domainObject">
|
||||||
|
</mct-representation>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
55
platform/commonUI/general/src/actions/MCTPreviewAction.js
Normal file
55
platform/commonUI/general/src/actions/MCTPreviewAction.js
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2017, 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 () {
|
||||||
|
|
||||||
|
var PREVIEW_TEMPLATE = '<mct-representation key="\'mct-preview\'"' +
|
||||||
|
'class="t-rep-frame holder"' +
|
||||||
|
'mct-object="domainObject">' +
|
||||||
|
'</mct-representation>';
|
||||||
|
|
||||||
|
function MCTPreviewAction($compile, $rootScope, context) {
|
||||||
|
context = context || {};
|
||||||
|
this.domainObject = context.selectedObject || context.domainObject;
|
||||||
|
this.$rootScope = $rootScope;
|
||||||
|
this.$compile = $compile;
|
||||||
|
}
|
||||||
|
|
||||||
|
MCTPreviewAction.prototype.perform = function () {
|
||||||
|
var newScope = this.$rootScope.$new();
|
||||||
|
newScope.domainObject = this.domainObject;
|
||||||
|
|
||||||
|
this.$compile(PREVIEW_TEMPLATE)(newScope);
|
||||||
|
};
|
||||||
|
|
||||||
|
MCTPreviewAction.appliesTo = function (context) {
|
||||||
|
var domainObject = (context || {}).domainObject,
|
||||||
|
status = domainObject.getCapability('status');
|
||||||
|
|
||||||
|
return !(status && status.get('editing'));
|
||||||
|
};
|
||||||
|
|
||||||
|
return MCTPreviewAction;
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module defining ContextMenuController. Created by vwoeltje on 11/17/14.
|
||||||
|
*/
|
||||||
|
define(
|
||||||
|
[],
|
||||||
|
function () {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller for the context menu. Maintains an up-to-date
|
||||||
|
* list of applicable actions (those from category "contextual")
|
||||||
|
*
|
||||||
|
* @memberof platform/commonUI/general
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
function ContextMenuController($scope) {
|
||||||
|
// Refresh variable "menuActions" in the scope
|
||||||
|
function updateActions() {
|
||||||
|
$scope.menuActions = $scope.action ?
|
||||||
|
$scope.action.getActions({ category: 'contextual' }) :
|
||||||
|
[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update using the action capability
|
||||||
|
$scope.$watch("action", updateActions);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ContextMenuController;
|
||||||
|
}
|
||||||
|
);
|
||||||
64
platform/commonUI/general/src/directives/MCTPreview.js
Normal file
64
platform/commonUI/general/src/directives/MCTPreview.js
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2016, 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(['zepto', '../services/Overlay'], function ($, Overlay) {
|
||||||
|
function MCTPreview($document) {
|
||||||
|
|
||||||
|
function link($scope, $element) {
|
||||||
|
var actions = $scope.domainObject.getCapability('action'),
|
||||||
|
notebookAction = actions.getActions({key: 'notebook-new-entry'})[0];
|
||||||
|
|
||||||
|
var notebookButton = notebookAction ?
|
||||||
|
[
|
||||||
|
{
|
||||||
|
class: 'icon-notebook new-notebook-entry',
|
||||||
|
title: 'New Notebook Entry',
|
||||||
|
clickHandler: function (event) {
|
||||||
|
event.stopPropagation();
|
||||||
|
notebookAction.perform();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
] : [];
|
||||||
|
|
||||||
|
var overlayService = new Overlay({
|
||||||
|
$document: $document,
|
||||||
|
$element: $element[0],
|
||||||
|
$scope: $scope,
|
||||||
|
browseBarButtons: notebookButton
|
||||||
|
});
|
||||||
|
|
||||||
|
overlayService.toggleOverlay();
|
||||||
|
|
||||||
|
$scope.$on('$destroy', function () {
|
||||||
|
$element.remove();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
restrict: 'A',
|
||||||
|
link: link
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return MCTPreview;
|
||||||
|
|
||||||
|
});
|
||||||
@@ -82,7 +82,7 @@ define(
|
|||||||
}
|
}
|
||||||
var searchPath = "?" + arr.join('&'),
|
var searchPath = "?" + arr.join('&'),
|
||||||
newTabPath =
|
newTabPath =
|
||||||
"#" + this.urlForLocation(mode, domainObject) +
|
"index.html#" + this.urlForLocation(mode, domainObject) +
|
||||||
searchPath;
|
searchPath;
|
||||||
return newTabPath;
|
return newTabPath;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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(
|
||||||
|
["../../src/controllers/ContextMenuController"],
|
||||||
|
function (ContextMenuController) {
|
||||||
|
|
||||||
|
describe("The context menu controller", function () {
|
||||||
|
var mockScope,
|
||||||
|
mockActions,
|
||||||
|
controller;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
mockActions = jasmine.createSpyObj("action", ["getActions"]);
|
||||||
|
mockScope = jasmine.createSpyObj("$scope", ["$watch"]);
|
||||||
|
controller = new ContextMenuController(mockScope);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("watches scope that may change applicable actions", function () {
|
||||||
|
// The action capability
|
||||||
|
expect(mockScope.$watch).toHaveBeenCalledWith(
|
||||||
|
"action",
|
||||||
|
jasmine.any(Function)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("populates the scope with grouped and ungrouped actions", function () {
|
||||||
|
mockScope.action = mockActions;
|
||||||
|
mockScope.parameters = { category: "test" };
|
||||||
|
|
||||||
|
mockActions.getActions.and.returnValue(["a", "b", "c"]);
|
||||||
|
|
||||||
|
// Call the watch
|
||||||
|
mockScope.$watch.calls.mostRecent().args[1]();
|
||||||
|
|
||||||
|
// Should have grouped and ungrouped actions in scope now
|
||||||
|
expect(mockScope.menuActions.length).toEqual(3);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -19,14 +19,16 @@
|
|||||||
this source code distribution or the Licensing information page available
|
this source code distribution or the Licensing information page available
|
||||||
at runtime from the About dialog for additional information.
|
at runtime from the About dialog for additional information.
|
||||||
-->
|
-->
|
||||||
<div class="c-clock l-time-display" ng-controller="ClockController as clock">
|
<div class="l-time-display l-digital l-clock s-clock" ng-controller="ClockController as clock">
|
||||||
<div class="c-clock__timezone">
|
<div class="l-elem-wrapper">
|
||||||
{{clock.zone()}}
|
<span class="l-elem timezone">
|
||||||
</div>
|
{{clock.zone()}}
|
||||||
<div class="c-clock__value">
|
</span>
|
||||||
{{clock.text()}}
|
<span class="l-elem value active">
|
||||||
</div>
|
{{clock.text()}}
|
||||||
<div class="c-clock__ampm">
|
</span>
|
||||||
{{clock.ampm()}}
|
<span class="l-elem ampm">
|
||||||
|
{{clock.ampm()}}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -19,19 +19,21 @@
|
|||||||
this source code distribution or the Licensing information page available
|
this source code distribution or the Licensing information page available
|
||||||
at runtime from the About dialog for additional information.
|
at runtime from the About dialog for additional information.
|
||||||
-->
|
-->
|
||||||
<div class="c-timer is-{{timer.timerState}}" ng-controller="TimerController as timer">
|
<div class="l-time-display l-digital l-timer s-timer s-state-{{timer.timerState}}" ng-controller="TimerController as timer">
|
||||||
<div class="c-timer__controls">
|
<div class="l-elem-wrapper l-flex-row">
|
||||||
<button ng-click="timer.clickStopButton()"
|
<div class="l-elem-wrapper l-flex-row controls">
|
||||||
ng-hide="timer.timerState == 'stopped'"
|
<a ng-click="timer.clickStopButton()"
|
||||||
title="Reset"
|
title="Stop"
|
||||||
class="c-timer__ctrl-reset c-click-icon c-click-icon--major icon-reset"></button>
|
class="flex-elem s-icon-button t-btn-stop icon-box"></a>
|
||||||
<button ng-click="timer.clickButton()"
|
<a ng-click="timer.clickButton()"
|
||||||
title="{{timer.buttonText()}}"
|
title="{{timer.buttonText()}}"
|
||||||
class="c-timer__ctrl-pause-play c-click-icon c-click-icon--major {{timer.buttonCssClass()}}"></button>
|
class="flex-elem s-icon-button t-btn-pauseplay {{timer.buttonCssClass()}}"></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="c-timer__direction {{timer.signClass()}}"
|
<span class="flex-elem l-value {{timer.signClass()}}">
|
||||||
ng-hide="!timer.signClass()"></div>
|
<span class="value"
|
||||||
<div class="c-timer__value">{{timer.text() || "--:--:--"}}
|
ng-class="{ active:timer.text() }">{{timer.text() || "--:--:--"}}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span ng-controller="RefreshingController"></span>
|
||||||
</div>
|
</div>
|
||||||
<span class="c-timer__ng-controller u-contents" ng-controller="RefreshingController"></span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ define([
|
|||||||
"./src/controllers/CompositeController",
|
"./src/controllers/CompositeController",
|
||||||
"./src/controllers/ColorController",
|
"./src/controllers/ColorController",
|
||||||
"./src/controllers/DialogButtonController",
|
"./src/controllers/DialogButtonController",
|
||||||
"./src/controllers/SnapshotPreviewController",
|
|
||||||
"./res/templates/controls/autocomplete.html",
|
"./res/templates/controls/autocomplete.html",
|
||||||
"./res/templates/controls/checkbox.html",
|
"./res/templates/controls/checkbox.html",
|
||||||
"./res/templates/controls/datetime.html",
|
"./res/templates/controls/datetime.html",
|
||||||
@@ -45,7 +44,6 @@ define([
|
|||||||
"./res/templates/controls/dialog.html",
|
"./res/templates/controls/dialog.html",
|
||||||
"./res/templates/controls/radio.html",
|
"./res/templates/controls/radio.html",
|
||||||
"./res/templates/controls/file-input.html",
|
"./res/templates/controls/file-input.html",
|
||||||
"./res/templates/controls/snap-view.html",
|
|
||||||
'legacyRegistry'
|
'legacyRegistry'
|
||||||
], function (
|
], function (
|
||||||
MCTForm,
|
MCTForm,
|
||||||
@@ -57,7 +55,6 @@ define([
|
|||||||
CompositeController,
|
CompositeController,
|
||||||
ColorController,
|
ColorController,
|
||||||
DialogButtonController,
|
DialogButtonController,
|
||||||
SnapshotPreviewController,
|
|
||||||
autocompleteTemplate,
|
autocompleteTemplate,
|
||||||
checkboxTemplate,
|
checkboxTemplate,
|
||||||
datetimeTemplate,
|
datetimeTemplate,
|
||||||
@@ -72,7 +69,6 @@ define([
|
|||||||
dialogTemplate,
|
dialogTemplate,
|
||||||
radioTemplate,
|
radioTemplate,
|
||||||
fileInputTemplate,
|
fileInputTemplate,
|
||||||
snapViewTemplate,
|
|
||||||
legacyRegistry
|
legacyRegistry
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@@ -157,10 +153,6 @@ define([
|
|||||||
{
|
{
|
||||||
"key": "file-input",
|
"key": "file-input",
|
||||||
"template": fileInputTemplate
|
"template": fileInputTemplate
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "snap-view",
|
|
||||||
"template": snapViewTemplate
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"controllers": [
|
"controllers": [
|
||||||
@@ -194,14 +186,6 @@ define([
|
|||||||
"$scope",
|
"$scope",
|
||||||
"dialogService"
|
"dialogService"
|
||||||
]
|
]
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "SnapshotPreviewController",
|
|
||||||
"implementation": SnapshotPreviewController,
|
|
||||||
"depends": [
|
|
||||||
"$scope",
|
|
||||||
"openmct"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"components": [
|
"components": [
|
||||||
|
|||||||
@@ -1,120 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* Open MCT, Copyright (c) 2014-2018, United States Government
|
|
||||||
* as represented by the Administrator of the National Aeronautics and Space
|
|
||||||
* Administration. All rights reserved.
|
|
||||||
*
|
|
||||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
|
||||||
* "License"); you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
* License for the specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
*
|
|
||||||
* Open MCT includes source code licensed under additional open source
|
|
||||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
|
||||||
* this source code distribution or the Licensing information page available
|
|
||||||
* at runtime from the About dialog for additional information.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
define(
|
|
||||||
[
|
|
||||||
'painterro'
|
|
||||||
],
|
|
||||||
function (Painterro) {
|
|
||||||
|
|
||||||
function SnapshotPreviewController($scope, openmct) {
|
|
||||||
|
|
||||||
$scope.previewImage = function (imageUrl) {
|
|
||||||
var image = document.createElement('img');
|
|
||||||
image.src = imageUrl;
|
|
||||||
|
|
||||||
openmct.overlays.overlay(
|
|
||||||
{
|
|
||||||
element: image,
|
|
||||||
size: 'large'
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.annotateImage = function (ngModel, field, imageUrl) {
|
|
||||||
$scope.imageUrl = imageUrl;
|
|
||||||
|
|
||||||
var div = document.createElement('div'),
|
|
||||||
painterroInstance = {},
|
|
||||||
save = false;
|
|
||||||
|
|
||||||
div.id = 'snap-annotation';
|
|
||||||
|
|
||||||
openmct.overlays.overlay(
|
|
||||||
{
|
|
||||||
element: div,
|
|
||||||
size: 'large',
|
|
||||||
buttons: [
|
|
||||||
{
|
|
||||||
label: 'Cancel',
|
|
||||||
callback: function () {
|
|
||||||
save = false;
|
|
||||||
painterroInstance.save();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Save',
|
|
||||||
callback: function () {
|
|
||||||
save = true;
|
|
||||||
painterroInstance.save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
painterroInstance = Painterro({
|
|
||||||
id: 'snap-annotation',
|
|
||||||
activeColor: '#ff0000',
|
|
||||||
activeColorAlpha: 1.0,
|
|
||||||
activeFillColor: '#fff',
|
|
||||||
activeFillColorAlpha: 0.0,
|
|
||||||
backgroundFillColor: '#000',
|
|
||||||
backgroundFillColorAlpha: 0.0,
|
|
||||||
defaultFontSize: 16,
|
|
||||||
defaultLineWidth: 2,
|
|
||||||
defaultTool: 'ellipse',
|
|
||||||
hiddenTools: ['save', 'open', 'close', 'eraser', 'pixelize', 'rotate', 'settings', 'resize'],
|
|
||||||
translation: {
|
|
||||||
name: 'en',
|
|
||||||
strings: {
|
|
||||||
lineColor: 'Line',
|
|
||||||
fillColor: 'Fill',
|
|
||||||
lineWidth: 'Size',
|
|
||||||
textColor: 'Color',
|
|
||||||
fontSize: 'Size',
|
|
||||||
fontStyle: 'Style'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
saveHandler: function (image, done) {
|
|
||||||
if (save) {
|
|
||||||
var url = image.asBlob(),
|
|
||||||
reader = new window.FileReader();
|
|
||||||
|
|
||||||
reader.readAsDataURL(url);
|
|
||||||
reader.onloadend = function () {
|
|
||||||
$scope.imageUrl = reader.result;
|
|
||||||
ngModel[field] = reader.result;
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
ngModel.field = imageUrl;
|
|
||||||
console.warn('You cancelled the annotation!!!');
|
|
||||||
}
|
|
||||||
done(true);
|
|
||||||
}
|
|
||||||
}).show(imageUrl);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return SnapshotPreviewController;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
@@ -25,10 +25,12 @@ define([
|
|||||||
"./src/MCTRepresentation",
|
"./src/MCTRepresentation",
|
||||||
"./src/gestures/DragGesture",
|
"./src/gestures/DragGesture",
|
||||||
"./src/gestures/DropGesture",
|
"./src/gestures/DropGesture",
|
||||||
|
"./src/gestures/ContextMenuGesture",
|
||||||
"./src/gestures/GestureProvider",
|
"./src/gestures/GestureProvider",
|
||||||
"./src/gestures/GestureRepresenter",
|
"./src/gestures/GestureRepresenter",
|
||||||
"./src/services/DndService",
|
"./src/services/DndService",
|
||||||
"./src/TemplateLinker",
|
"./src/TemplateLinker",
|
||||||
|
"./src/actions/ContextMenuAction",
|
||||||
"./src/TemplatePrefetcher",
|
"./src/TemplatePrefetcher",
|
||||||
'legacyRegistry'
|
'legacyRegistry'
|
||||||
], function (
|
], function (
|
||||||
@@ -36,10 +38,12 @@ define([
|
|||||||
MCTRepresentation,
|
MCTRepresentation,
|
||||||
DragGesture,
|
DragGesture,
|
||||||
DropGesture,
|
DropGesture,
|
||||||
|
ContextMenuGesture,
|
||||||
GestureProvider,
|
GestureProvider,
|
||||||
GestureRepresenter,
|
GestureRepresenter,
|
||||||
DndService,
|
DndService,
|
||||||
TemplateLinker,
|
TemplateLinker,
|
||||||
|
ContextMenuAction,
|
||||||
TemplatePrefetcher,
|
TemplatePrefetcher,
|
||||||
legacyRegistry
|
legacyRegistry
|
||||||
) {
|
) {
|
||||||
@@ -84,6 +88,14 @@ define([
|
|||||||
"dndService",
|
"dndService",
|
||||||
"$q"
|
"$q"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "menu",
|
||||||
|
"implementation": ContextMenuGesture,
|
||||||
|
"depends": [
|
||||||
|
"$timeout",
|
||||||
|
"agentService"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"components": [
|
"components": [
|
||||||
@@ -124,6 +136,19 @@ define([
|
|||||||
"comment": "For internal use by mct-include and mct-representation."
|
"comment": "For internal use by mct-include and mct-representation."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"key": "menu",
|
||||||
|
"implementation": ContextMenuAction,
|
||||||
|
"depends": [
|
||||||
|
"$compile",
|
||||||
|
"$document",
|
||||||
|
"$rootScope",
|
||||||
|
"popupService",
|
||||||
|
"agentService"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
"runs": [
|
"runs": [
|
||||||
{
|
{
|
||||||
"priority": "mandatory",
|
"priority": "mandatory",
|
||||||
|
|||||||
138
platform/representation/src/actions/ContextMenuAction.js
Normal file
138
platform/representation/src/actions/ContextMenuAction.js
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module defining ContextMenuAction. Created by shale on 06/30/2015.
|
||||||
|
*/
|
||||||
|
define(
|
||||||
|
["../gestures/GestureConstants"],
|
||||||
|
function (GestureConstants) {
|
||||||
|
|
||||||
|
var MENU_TEMPLATE = "<mct-representation key=\"'context-menu'\" " +
|
||||||
|
"mct-object=\"domainObject\" " +
|
||||||
|
"ng-class=\"menuClass\" " +
|
||||||
|
"ng-style=\"menuStyle\">" +
|
||||||
|
"</mct-representation>",
|
||||||
|
dismissExistingMenu;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Launches a custom context menu for the domain object it contains.
|
||||||
|
*
|
||||||
|
* @memberof platform/representation
|
||||||
|
* @constructor
|
||||||
|
* @param $compile Angular's $compile service
|
||||||
|
* @param $document the current document
|
||||||
|
* @param $rootScope Angular's root scope
|
||||||
|
* @param {platform/commonUI/general.PopupService} popupService
|
||||||
|
* @param actionContext the context in which the action
|
||||||
|
* should be performed
|
||||||
|
* @implements {Action}
|
||||||
|
*/
|
||||||
|
function ContextMenuAction(
|
||||||
|
$compile,
|
||||||
|
$document,
|
||||||
|
$rootScope,
|
||||||
|
popupService,
|
||||||
|
agentService,
|
||||||
|
actionContext
|
||||||
|
) {
|
||||||
|
this.$compile = $compile;
|
||||||
|
this.agentService = agentService;
|
||||||
|
this.actionContext = actionContext;
|
||||||
|
this.popupService = popupService;
|
||||||
|
this.getDocument = function () {
|
||||||
|
return $document;
|
||||||
|
};
|
||||||
|
this.getRootScope = function () {
|
||||||
|
return $rootScope;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ContextMenuAction.prototype.perform = function () {
|
||||||
|
var $compile = this.$compile,
|
||||||
|
$document = this.getDocument(),
|
||||||
|
$rootScope = this.getRootScope(),
|
||||||
|
actionContext = this.actionContext,
|
||||||
|
eventCoords = [
|
||||||
|
actionContext.event.pageX,
|
||||||
|
actionContext.event.pageY
|
||||||
|
],
|
||||||
|
menuDim = GestureConstants.MCT_MENU_DIMENSIONS,
|
||||||
|
body = $document.find('body'),
|
||||||
|
scope = $rootScope.$new(),
|
||||||
|
initiatingEvent = this.agentService.isMobile() ?
|
||||||
|
'touchstart' : 'mousedown',
|
||||||
|
menu,
|
||||||
|
popup;
|
||||||
|
|
||||||
|
// Remove the context menu
|
||||||
|
function dismiss() {
|
||||||
|
if (popup) {
|
||||||
|
popup.dismiss();
|
||||||
|
popup = undefined;
|
||||||
|
}
|
||||||
|
scope.$destroy();
|
||||||
|
body.off("mousedown", dismiss);
|
||||||
|
dismissExistingMenu = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dismiss any menu which was already showing
|
||||||
|
if (dismissExistingMenu) {
|
||||||
|
dismissExistingMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...and record the presence of this menu.
|
||||||
|
dismissExistingMenu = dismiss;
|
||||||
|
|
||||||
|
// Set up the scope, including menu positioning
|
||||||
|
scope.domainObject = actionContext.domainObject;
|
||||||
|
scope.menuClass = { "context-menu-holder": true };
|
||||||
|
// Create the context menu
|
||||||
|
menu = $compile(MENU_TEMPLATE)(scope);
|
||||||
|
|
||||||
|
popup = this.popupService.display(menu, eventCoords, {
|
||||||
|
marginX: -menuDim[0],
|
||||||
|
marginY: -menuDim[1]
|
||||||
|
});
|
||||||
|
|
||||||
|
scope.menuClass['go-left'] = popup.goesLeft();
|
||||||
|
scope.menuClass['go-up'] = popup.goesUp();
|
||||||
|
|
||||||
|
// Stop propagation so that clicks or touches on the menu do not close the menu
|
||||||
|
menu.on(initiatingEvent, function (event) {
|
||||||
|
event.stopPropagation();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Dismiss the menu when body is clicked/touched elsewhere
|
||||||
|
// ('mousedown' because 'click' breaks left-click context menus)
|
||||||
|
// ('touchstart' because 'touch' breaks context menus up)
|
||||||
|
body.on(initiatingEvent, dismiss);
|
||||||
|
// NOTE: Apply to mobile?
|
||||||
|
menu.on('click', dismiss);
|
||||||
|
|
||||||
|
// Don't launch browser's context menu
|
||||||
|
actionContext.event.preventDefault();
|
||||||
|
};
|
||||||
|
|
||||||
|
return ContextMenuAction;
|
||||||
|
}
|
||||||
|
);
|
||||||
100
platform/representation/src/gestures/ContextMenuGesture.js
Normal file
100
platform/representation/src/gestures/ContextMenuGesture.js
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module defining ContextMenuGesture.
|
||||||
|
* Created by vwoeltje on 11/17/14. Modified by shale on 06/30/2015.
|
||||||
|
*/
|
||||||
|
define(
|
||||||
|
function () {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add listeners to a representation such that it calls the
|
||||||
|
* context menu action for the domain object it contains.
|
||||||
|
*
|
||||||
|
* @memberof platform/representation
|
||||||
|
* @constructor
|
||||||
|
* @param element the jqLite-wrapped element which should exhibit
|
||||||
|
* the context menu
|
||||||
|
* @param {DomainObject} domainObject the object on which actions
|
||||||
|
* in the context menu will be performed
|
||||||
|
* @implements {Gesture}
|
||||||
|
*/
|
||||||
|
function ContextMenuGesture($timeout, agentService, element, domainObject) {
|
||||||
|
var isPressing,
|
||||||
|
isDragging,
|
||||||
|
longTouchTime = 500;
|
||||||
|
|
||||||
|
function showMenu(event) {
|
||||||
|
domainObject.getCapability('action').perform({
|
||||||
|
key: 'menu',
|
||||||
|
domainObject: domainObject,
|
||||||
|
event: event
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// When context menu event occurs, show object actions instead
|
||||||
|
if (!agentService.isMobile()) {
|
||||||
|
|
||||||
|
// When context menu event occurs, show object actions instead
|
||||||
|
element.on('contextmenu', showMenu);
|
||||||
|
} else if (agentService.isMobile()) {
|
||||||
|
|
||||||
|
// If on mobile device, then start timeout for the single touch event
|
||||||
|
// during the timeout 'isPressing' is true.
|
||||||
|
element.on('touchstart', function (event) {
|
||||||
|
if (event.touches.length < 2) {
|
||||||
|
isPressing = true;
|
||||||
|
|
||||||
|
// After the timeout, if 'isPressing' is
|
||||||
|
// true, display context menu for object
|
||||||
|
$timeout(function () {
|
||||||
|
if (isPressing && !isDragging) {
|
||||||
|
showMenu(event);
|
||||||
|
}
|
||||||
|
}, longTouchTime);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// If on Mobile Device, and user scrolls/drags set flag to true
|
||||||
|
element.on('touchmove', function () {
|
||||||
|
isDragging = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Whenever the touch event ends, 'isPressing' & 'isDragging' is false.
|
||||||
|
element.on('touchend', function () {
|
||||||
|
isPressing = false;
|
||||||
|
isDragging = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.showMenuCallback = showMenu;
|
||||||
|
this.element = element;
|
||||||
|
}
|
||||||
|
|
||||||
|
ContextMenuGesture.prototype.destroy = function () {
|
||||||
|
this.element.off('contextmenu', this.showMenu);
|
||||||
|
};
|
||||||
|
|
||||||
|
return ContextMenuGesture;
|
||||||
|
}
|
||||||
|
);
|
||||||
202
platform/representation/test/actions/ContextMenuActionSpec.js
Normal file
202
platform/representation/test/actions/ContextMenuActionSpec.js
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module defining ContextMenuActionSpec. Created by shale on 07/02/2015.
|
||||||
|
*/
|
||||||
|
define(
|
||||||
|
["../../src/actions/ContextMenuAction"],
|
||||||
|
function (ContextMenuAction) {
|
||||||
|
|
||||||
|
var JQLITE_FUNCTIONS = ["on", "off", "find", "append", "remove"],
|
||||||
|
DOMAIN_OBJECT_METHODS = ["getId", "getModel", "getCapability", "hasCapability", "useCapability"];
|
||||||
|
|
||||||
|
|
||||||
|
describe("The 'context menu' action", function () {
|
||||||
|
var mockCompile,
|
||||||
|
mockCompiledTemplate,
|
||||||
|
mockMenu,
|
||||||
|
mockDocument,
|
||||||
|
mockBody,
|
||||||
|
mockPopupService,
|
||||||
|
mockRootScope,
|
||||||
|
mockAgentService,
|
||||||
|
mockScope,
|
||||||
|
mockDomainObject,
|
||||||
|
mockEvent,
|
||||||
|
mockPopup,
|
||||||
|
mockActionContext,
|
||||||
|
action;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
mockCompile = jasmine.createSpy("$compile");
|
||||||
|
mockCompiledTemplate = jasmine.createSpy("template");
|
||||||
|
mockMenu = jasmine.createSpyObj("menu", JQLITE_FUNCTIONS);
|
||||||
|
mockDocument = jasmine.createSpyObj("$document", JQLITE_FUNCTIONS);
|
||||||
|
mockBody = jasmine.createSpyObj("body", JQLITE_FUNCTIONS);
|
||||||
|
mockPopupService =
|
||||||
|
jasmine.createSpyObj("popupService", ["display"]);
|
||||||
|
mockPopup = jasmine.createSpyObj("popup", [
|
||||||
|
"dismiss",
|
||||||
|
"goesLeft",
|
||||||
|
"goesUp"
|
||||||
|
]);
|
||||||
|
mockRootScope = jasmine.createSpyObj("$rootScope", ["$new"]);
|
||||||
|
mockAgentService = jasmine.createSpyObj("agentService", ["isMobile"]);
|
||||||
|
mockScope = jasmine.createSpyObj("scope", ["$destroy"]);
|
||||||
|
mockDomainObject = jasmine.createSpyObj("domainObject", DOMAIN_OBJECT_METHODS);
|
||||||
|
mockEvent = jasmine.createSpyObj("event", ["preventDefault", "stopPropagation"]);
|
||||||
|
mockEvent.pageX = 123;
|
||||||
|
mockEvent.pageY = 321;
|
||||||
|
|
||||||
|
mockCompile.and.returnValue(mockCompiledTemplate);
|
||||||
|
mockCompiledTemplate.and.returnValue(mockMenu);
|
||||||
|
mockDocument.find.and.returnValue(mockBody);
|
||||||
|
mockRootScope.$new.and.returnValue(mockScope);
|
||||||
|
mockPopupService.display.and.returnValue(mockPopup);
|
||||||
|
|
||||||
|
mockActionContext = {key: 'menu', domainObject: mockDomainObject, event: mockEvent};
|
||||||
|
|
||||||
|
action = new ContextMenuAction(
|
||||||
|
mockCompile,
|
||||||
|
mockDocument,
|
||||||
|
mockRootScope,
|
||||||
|
mockPopupService,
|
||||||
|
mockAgentService,
|
||||||
|
mockActionContext
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("displays a popup when performed", function () {
|
||||||
|
action.perform();
|
||||||
|
expect(mockPopupService.display).toHaveBeenCalledWith(
|
||||||
|
mockMenu,
|
||||||
|
[mockEvent.pageX, mockEvent.pageY],
|
||||||
|
jasmine.any(Object)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("prevents the default context menu behavior", function () {
|
||||||
|
action.perform();
|
||||||
|
expect(mockEvent.preventDefault).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("adds classes to menus based on position", function () {
|
||||||
|
var booleans = [false, true];
|
||||||
|
|
||||||
|
booleans.forEach(function (goLeft) {
|
||||||
|
booleans.forEach(function (goUp) {
|
||||||
|
mockPopup.goesLeft.and.returnValue(goLeft);
|
||||||
|
mockPopup.goesUp.and.returnValue(goUp);
|
||||||
|
action.perform();
|
||||||
|
expect(!!mockScope.menuClass['go-up'])
|
||||||
|
.toEqual(goUp);
|
||||||
|
expect(!!mockScope.menuClass['go-left'])
|
||||||
|
.toEqual(goLeft);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it("removes a menu when body is clicked", function () {
|
||||||
|
// Show the menu
|
||||||
|
action.perform();
|
||||||
|
|
||||||
|
// Verify precondition
|
||||||
|
expect(mockBody.remove).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
// Find and fire body's mousedown listener
|
||||||
|
mockBody.on.calls.all().forEach(function (call) {
|
||||||
|
if (call.args[0] === 'mousedown') {
|
||||||
|
call.args[1]();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Menu should have been removed
|
||||||
|
expect(mockPopup.dismiss).toHaveBeenCalled();
|
||||||
|
|
||||||
|
// Listener should have been detached from body
|
||||||
|
expect(mockBody.off).toHaveBeenCalledWith(
|
||||||
|
'mousedown',
|
||||||
|
jasmine.any(Function)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("removes a menu when it is clicked", function () {
|
||||||
|
// Show the menu
|
||||||
|
action.perform();
|
||||||
|
|
||||||
|
// Verify precondition
|
||||||
|
expect(mockMenu.remove).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
// Find and fire menu's click listener
|
||||||
|
mockMenu.on.calls.all().forEach(function (call) {
|
||||||
|
if (call.args[0] === 'click') {
|
||||||
|
call.args[1]();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Menu should have been removed
|
||||||
|
expect(mockPopup.dismiss).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("keeps a menu when menu is clicked", function () {
|
||||||
|
// Show the menu
|
||||||
|
action.perform();
|
||||||
|
// Find and fire body's mousedown listener
|
||||||
|
mockMenu.on.calls.all().forEach(function (call) {
|
||||||
|
if (call.args[0] === 'mousedown') {
|
||||||
|
call.args[1](mockEvent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Menu should have been removed
|
||||||
|
expect(mockPopup.dismiss).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
// Listener should have been detached from body
|
||||||
|
expect(mockBody.off).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("keeps a menu when menu is clicked on mobile", function () {
|
||||||
|
mockAgentService.isMobile.and.returnValue(true);
|
||||||
|
action = new ContextMenuAction(
|
||||||
|
mockCompile,
|
||||||
|
mockDocument,
|
||||||
|
mockRootScope,
|
||||||
|
mockPopupService,
|
||||||
|
mockAgentService,
|
||||||
|
mockActionContext
|
||||||
|
);
|
||||||
|
action.perform();
|
||||||
|
|
||||||
|
mockMenu.on.calls.all().forEach(function (call) {
|
||||||
|
if (call.args[0] === 'touchstart') {
|
||||||
|
call.args[1](mockEvent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(mockPopup.dismiss).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
119
platform/representation/test/gestures/ContextMenuGestureSpec.js
Normal file
119
platform/representation/test/gestures/ContextMenuGestureSpec.js
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module defining ContextMenuGestureSpec. Created by vwoeltje on 11/22/14.
|
||||||
|
*/
|
||||||
|
define(
|
||||||
|
["../../src/gestures/ContextMenuGesture"],
|
||||||
|
function (ContextMenuGesture) {
|
||||||
|
|
||||||
|
var JQLITE_FUNCTIONS = ["on", "off", "find", "append", "remove"],
|
||||||
|
DOMAIN_OBJECT_METHODS = ["getId", "getModel", "getCapability", "hasCapability", "useCapability"];
|
||||||
|
|
||||||
|
|
||||||
|
describe("The 'context menu' gesture", function () {
|
||||||
|
var mockTimeout,
|
||||||
|
mockElement,
|
||||||
|
mockAgentService,
|
||||||
|
mockDomainObject,
|
||||||
|
mockTouchEvent,
|
||||||
|
mockContextMenuAction,
|
||||||
|
mockTouch,
|
||||||
|
gesture,
|
||||||
|
fireGesture,
|
||||||
|
fireTouchStartGesture,
|
||||||
|
fireTouchEndGesture;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
mockTimeout = jasmine.createSpy("$timeout");
|
||||||
|
mockElement = jasmine.createSpyObj("element", JQLITE_FUNCTIONS);
|
||||||
|
mockAgentService = jasmine.createSpyObj("agentService", ["isMobile"]);
|
||||||
|
mockDomainObject = jasmine.createSpyObj("domainObject", DOMAIN_OBJECT_METHODS);
|
||||||
|
mockContextMenuAction = jasmine.createSpyObj(
|
||||||
|
"action",
|
||||||
|
["perform", "getActions"]
|
||||||
|
);
|
||||||
|
|
||||||
|
mockDomainObject.getCapability.and.returnValue(mockContextMenuAction);
|
||||||
|
mockContextMenuAction.perform.and.returnValue(jasmine.any(Function));
|
||||||
|
mockAgentService.isMobile.and.returnValue(false);
|
||||||
|
|
||||||
|
|
||||||
|
gesture = new ContextMenuGesture(mockTimeout, mockAgentService, mockElement, mockDomainObject);
|
||||||
|
|
||||||
|
// Capture the contextmenu callback
|
||||||
|
fireGesture = mockElement.on.calls.mostRecent().args[1];
|
||||||
|
});
|
||||||
|
|
||||||
|
it("attaches a callback for context menu events", function () {
|
||||||
|
// Fire a click and expect it to happen
|
||||||
|
fireGesture();
|
||||||
|
expect(mockElement.on).toHaveBeenCalledWith(
|
||||||
|
"contextmenu",
|
||||||
|
jasmine.any(Function)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("detaches a callback for context menu events when destroyed", function () {
|
||||||
|
expect(mockElement.off).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
gesture.destroy();
|
||||||
|
|
||||||
|
expect(mockElement.off).toHaveBeenCalledWith(
|
||||||
|
"contextmenu",
|
||||||
|
//mockElement.on.calls.mostRecent().args[1]
|
||||||
|
mockDomainObject.calls
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("attaches a callback for context menu events on mobile", function () {
|
||||||
|
// Mock touch event and set to mobile device
|
||||||
|
mockTouchEvent = jasmine.createSpyObj("event", ["preventDefault", "touches"]);
|
||||||
|
mockTouch = jasmine.createSpyObj("touch", ["length"]);
|
||||||
|
mockTouch.length = 1;
|
||||||
|
mockTouchEvent.touches.and.returnValue(mockTouch);
|
||||||
|
mockAgentService.isMobile.and.returnValue(true);
|
||||||
|
|
||||||
|
// Then create new (mobile) gesture
|
||||||
|
gesture = new ContextMenuGesture(mockTimeout, mockAgentService, mockElement, mockDomainObject);
|
||||||
|
|
||||||
|
// Set calls for the touchstart and touchend gestures
|
||||||
|
fireTouchStartGesture = mockElement.on.calls.all()[1].args[1];
|
||||||
|
fireTouchEndGesture = mockElement.on.calls.mostRecent().args[1];
|
||||||
|
|
||||||
|
// Fire touchstart and expect touch start to begin
|
||||||
|
fireTouchStartGesture(mockTouchEvent);
|
||||||
|
expect(mockElement.on).toHaveBeenCalledWith(
|
||||||
|
"touchstart",
|
||||||
|
jasmine.any(Function)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Expect timeout to begin and then fireTouchEnd
|
||||||
|
expect(mockTimeout).toHaveBeenCalled();
|
||||||
|
mockTimeout.calls.mostRecent().args[0]();
|
||||||
|
fireTouchEndGesture(mockTouchEvent);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -40,10 +40,9 @@ define([
|
|||||||
'../platform/framework/src/Main',
|
'../platform/framework/src/Main',
|
||||||
'./styles-new/core.scss',
|
'./styles-new/core.scss',
|
||||||
'./styles-new/notebook.scss',
|
'./styles-new/notebook.scss',
|
||||||
'./ui/layout/Layout.vue',
|
'./ui/components/layout/Layout.vue',
|
||||||
'../platform/core/src/objects/DomainObjectImpl',
|
'../platform/core/src/objects/DomainObjectImpl',
|
||||||
'../platform/core/src/capabilities/ContextualDomainObject',
|
'../platform/core/src/capabilities/ContextualDomainObject',
|
||||||
'./ui/preview/plugin',
|
|
||||||
'vue'
|
'vue'
|
||||||
], function (
|
], function (
|
||||||
EventEmitter,
|
EventEmitter,
|
||||||
@@ -68,7 +67,6 @@ define([
|
|||||||
Layout,
|
Layout,
|
||||||
DomainObjectImpl,
|
DomainObjectImpl,
|
||||||
ContextualDomainObject,
|
ContextualDomainObject,
|
||||||
PreviewPlugin,
|
|
||||||
Vue
|
Vue
|
||||||
) {
|
) {
|
||||||
/**
|
/**
|
||||||
@@ -232,7 +230,6 @@ define([
|
|||||||
this.install(this.plugins.Plot());
|
this.install(this.plugins.Plot());
|
||||||
this.install(this.plugins.TelemetryTable());
|
this.install(this.plugins.TelemetryTable());
|
||||||
this.install(this.plugins.DisplayLayout());
|
this.install(this.plugins.DisplayLayout());
|
||||||
this.install(PreviewPlugin.default());
|
|
||||||
|
|
||||||
if (typeof BUILD_CONSTANTS !== 'undefined') {
|
if (typeof BUILD_CONSTANTS !== 'undefined') {
|
||||||
this.install(buildInfoPlugin(BUILD_CONSTANTS));
|
this.install(buildInfoPlugin(BUILD_CONSTANTS));
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import LegacyContextMenuAction from './LegacyContextMenuAction';
|
|||||||
|
|
||||||
export default function LegacyActionAdapter(openmct, legacyActions) {
|
export default function LegacyActionAdapter(openmct, legacyActions) {
|
||||||
function contextualCategoryOnly(action) {
|
function contextualCategoryOnly(action) {
|
||||||
if (action.category === 'contextual' || (Array.isArray(action.category) && action.category.includes('contextual'))) {
|
if (action.category === 'contextual') {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
console.warn(`DEPRECATION WARNING: Action ${action.definition.key} in bundle ${action.bundle.path} is non-contextual and should be migrated.`);
|
console.warn(`DEPRECATION WARNING: Action ${action.definition.key} in bundle ${action.bundle.path} is non-contextual and should be migrated.`);
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { timingSafeEqual } from "crypto";
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
* Open MCT, Copyright (c) 2014-2018, United States Government
|
* Open MCT, Copyright (c) 2014-2018, United States Government
|
||||||
* as represented by the Administrator of the National Aeronautics and Space
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
@@ -19,9 +21,6 @@
|
|||||||
* this source code distribution or the Licensing information page available
|
* this source code distribution or the Licensing information page available
|
||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
import _ from 'lodash';
|
|
||||||
const INSIDE_EDIT_PATH_BLACKLIST = ["copy", "follow", "link", "locate", "move", "link"];
|
|
||||||
const OUTSIDE_EDIT_PATH_BLACKLIST = ["copy", "follow", "properties", "move", "link", "remove", "locate"];
|
|
||||||
|
|
||||||
export default class LegacyContextMenuAction {
|
export default class LegacyContextMenuAction {
|
||||||
constructor(openmct, LegacyAction) {
|
constructor(openmct, LegacyAction) {
|
||||||
@@ -32,6 +31,13 @@ export default class LegacyContextMenuAction {
|
|||||||
this.LegacyAction = LegacyAction;
|
this.LegacyAction = LegacyAction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
appliesTo(objectPath) {
|
||||||
|
let legacyObject = this.openmct.legacyObject(objectPath);
|
||||||
|
return this.LegacyAction.appliesTo({
|
||||||
|
domainObject: legacyObject
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
invoke(objectPath) {
|
invoke(objectPath) {
|
||||||
let context = {
|
let context = {
|
||||||
category: 'contextual',
|
category: 'contextual',
|
||||||
@@ -48,36 +54,4 @@ export default class LegacyContextMenuAction {
|
|||||||
}
|
}
|
||||||
legacyAction.perform();
|
legacyAction.perform();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
appliesTo(objectPath) {
|
|
||||||
let legacyObject = this.openmct.legacyObject(objectPath);
|
|
||||||
|
|
||||||
return (this.LegacyAction.appliesTo === undefined ||
|
|
||||||
this.LegacyAction.appliesTo({domainObject: legacyObject})) &&
|
|
||||||
!this.isBlacklisted(objectPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
isBlacklisted(objectPath) {
|
|
||||||
let navigatedObject = this.openmct.router.path[0];
|
|
||||||
let isEditing = this.openmct.editor.isEditing();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Is the object being edited, or a child of the object being edited?
|
|
||||||
*/
|
|
||||||
function isInsideEditPath() {
|
|
||||||
return objectPath.some((object) => _.eq(object.identifier, navigatedObject.identifier));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isEditing) {
|
|
||||||
if (isInsideEditPath()) {
|
|
||||||
return INSIDE_EDIT_PATH_BLACKLIST.some(actionKey => this.LegacyAction.key === actionKey);
|
|
||||||
} else {
|
|
||||||
return OUTSIDE_EDIT_PATH_BLACKLIST.some(actionKey => this.LegacyAction.key === actionKey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -58,8 +58,11 @@ define([
|
|||||||
|
|
||||||
handleLegacyMutation = function (legacyObject) {
|
handleLegacyMutation = function (legacyObject) {
|
||||||
var newStyleObject = utils.toNewFormat(legacyObject.getModel(), legacyObject.getId());
|
var newStyleObject = utils.toNewFormat(legacyObject.getModel(), legacyObject.getId());
|
||||||
|
|
||||||
|
//Don't trigger self
|
||||||
|
this.eventEmitter.off('mutation', handleMutation);
|
||||||
this.eventEmitter.emit(newStyleObject.identifier.key + ":*", newStyleObject);
|
this.eventEmitter.emit(newStyleObject.identifier.key + ":*", newStyleObject);
|
||||||
this.eventEmitter.emit('mutation', newStyleObject);
|
this.eventEmitter.on('mutation', handleMutation);
|
||||||
}.bind(this);
|
}.bind(this);
|
||||||
|
|
||||||
this.eventEmitter.on('mutation', handleMutation);
|
this.eventEmitter.on('mutation', handleMutation);
|
||||||
|
|||||||
@@ -21,9 +21,7 @@ define([
|
|||||||
name: legacyView.name,
|
name: legacyView.name,
|
||||||
cssClass: legacyView.cssClass,
|
cssClass: legacyView.cssClass,
|
||||||
description: legacyView.description,
|
description: legacyView.description,
|
||||||
canEdit: function () {
|
editable: legacyView.editable,
|
||||||
return legacyView.editable === true;
|
|
||||||
},
|
|
||||||
canView: function (domainObject) {
|
canView: function (domainObject) {
|
||||||
if (!domainObject || !domainObject.identifier) {
|
if (!domainObject || !domainObject.identifier) {
|
||||||
return false;
|
return false;
|
||||||
@@ -79,7 +77,7 @@ define([
|
|||||||
openmct.$angular.element(container),
|
openmct.$angular.element(container),
|
||||||
legacyView
|
legacyView
|
||||||
);
|
);
|
||||||
container.classList.add('u-contents');
|
container.style.height = '100%';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (promises.length) {
|
if (promises.length) {
|
||||||
|
|||||||
@@ -28,11 +28,6 @@ export default class Editor extends EventEmitter {
|
|||||||
super();
|
super();
|
||||||
this.editing = false;
|
this.editing = false;
|
||||||
this.openmct = openmct;
|
this.openmct = openmct;
|
||||||
document.addEventListener('drop', (event) => {
|
|
||||||
if (!this.isEditing()) {
|
|
||||||
this.edit();
|
|
||||||
}
|
|
||||||
}, {capture: true});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ define([
|
|||||||
function DefaultCompositionProvider(publicAPI, compositionAPI) {
|
function DefaultCompositionProvider(publicAPI, compositionAPI) {
|
||||||
this.publicAPI = publicAPI;
|
this.publicAPI = publicAPI;
|
||||||
this.listeningTo = {};
|
this.listeningTo = {};
|
||||||
this.onMutation = this.onMutation.bind(this);
|
|
||||||
|
|
||||||
this.cannotContainDuplicates = this.cannotContainDuplicates.bind(this);
|
this.cannotContainDuplicates = this.cannotContainDuplicates.bind(this);
|
||||||
this.cannotContainItself = this.cannotContainItself.bind(this);
|
this.cannotContainItself = this.cannotContainItself.bind(this);
|
||||||
@@ -209,10 +208,9 @@ define([
|
|||||||
if (this.topicListener) {
|
if (this.topicListener) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.publicAPI.objects.eventEmitter.on('mutation', this.onMutation);
|
var topic = this.publicAPI.$injector.get('topic');
|
||||||
this.topicListener = () => {
|
var mutation = topic('mutation');
|
||||||
this.publicAPI.objects.eventEmitter.off('mutation', this.onMutation)
|
this.topicListener = mutation.listen(this.onMutation.bind(this));
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -222,7 +220,7 @@ define([
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
DefaultCompositionProvider.prototype.onMutation = function (oldDomainObject) {
|
DefaultCompositionProvider.prototype.onMutation = function (oldDomainObject) {
|
||||||
var id = objectUtils.makeKeyString(oldDomainObject.identifier);
|
var id = oldDomainObject.getId();
|
||||||
var listeners = this.listeningTo[id];
|
var listeners = this.listeningTo[id];
|
||||||
|
|
||||||
if (!listeners) {
|
if (!listeners) {
|
||||||
@@ -230,7 +228,7 @@ define([
|
|||||||
}
|
}
|
||||||
|
|
||||||
var oldComposition = listeners.composition.map(objectUtils.makeKeyString);
|
var oldComposition = listeners.composition.map(objectUtils.makeKeyString);
|
||||||
var newComposition = oldDomainObject.composition.map(objectUtils.makeKeyString);
|
var newComposition = oldDomainObject.getModel().composition.map(objectUtils.makeKeyString);
|
||||||
|
|
||||||
var added = _.difference(newComposition, oldComposition).map(objectUtils.parseKeyString);
|
var added = _.difference(newComposition, oldComposition).map(objectUtils.parseKeyString);
|
||||||
var removed = _.difference(oldComposition, newComposition).map(objectUtils.parseKeyString);
|
var removed = _.difference(oldComposition, newComposition).map(objectUtils.parseKeyString);
|
||||||
|
|||||||
@@ -73,12 +73,8 @@ class ContextMenuAPI {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_showContextMenuForObjectPath(objectPath, x, y) {
|
_showContextMenuForObjectPath(objectPath, x, y) {
|
||||||
let applicableActions = this._allActions.filter((action) => {
|
let applicableActions = this._allActions.filter(
|
||||||
if (action.appliesTo === undefined) {
|
(action) => action.appliesTo(objectPath));
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return action.appliesTo(objectPath);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (this._activeContextMenu) {
|
if (this._activeContextMenu) {
|
||||||
this._hideActiveContextMenu();
|
this._hideActiveContextMenu();
|
||||||
|
|||||||
@@ -83,15 +83,18 @@ define([
|
|||||||
this.object = newObject;
|
this.object = newObject;
|
||||||
}.bind(this);
|
}.bind(this);
|
||||||
|
|
||||||
//Emit wildcard event
|
|
||||||
this.eventEmitter.emit(qualifiedEventName(this.object, '*'), this.object);
|
|
||||||
//Emit a general "any object" event
|
|
||||||
this.eventEmitter.emit(ANY_OBJECT_EVENT, this.object);
|
|
||||||
|
|
||||||
this.eventEmitter.on(qualifiedEventName(this.object, '*'), handleRecursiveMutation);
|
this.eventEmitter.on(qualifiedEventName(this.object, '*'), handleRecursiveMutation);
|
||||||
|
|
||||||
//Emit event specific to property
|
//Emit event specific to property
|
||||||
this.eventEmitter.emit(qualifiedEventName(this.object, path), value);
|
this.eventEmitter.emit(qualifiedEventName(this.object, path), value);
|
||||||
|
|
||||||
this.eventEmitter.off(qualifiedEventName(this.object, '*'), handleRecursiveMutation);
|
this.eventEmitter.off(qualifiedEventName(this.object, '*'), handleRecursiveMutation);
|
||||||
|
|
||||||
|
//Emit wildcare event
|
||||||
|
this.eventEmitter.emit(qualifiedEventName(this.object, '*'), this.object);
|
||||||
|
|
||||||
|
//Emit a general "any object" event
|
||||||
|
this.eventEmitter.emit(ANY_OBJECT_EVENT, this.object);
|
||||||
};
|
};
|
||||||
|
|
||||||
return MutableObject;
|
return MutableObject;
|
||||||
|
|||||||
@@ -3,15 +3,13 @@ import Overlay from './Overlay';
|
|||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
|
||||||
class Dialog extends Overlay {
|
class Dialog extends Overlay {
|
||||||
constructor({iconClass, message, title, hint, timestamp, ...options}) {
|
constructor({iconClass, message, title, ...options}) {
|
||||||
|
|
||||||
let component = new Vue({
|
let component = new Vue({
|
||||||
provide: {
|
provide: {
|
||||||
iconClass,
|
iconClass,
|
||||||
message,
|
message,
|
||||||
title,
|
title
|
||||||
hint,
|
|
||||||
timestamp
|
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
DialogComponent: DialogComponent
|
DialogComponent: DialogComponent
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import ProgressComponent from '../../ui/components/ProgressBar.vue';
|
import ProgressComponent from '../../ui/components/layout/ProgressBar.vue';
|
||||||
import Overlay from './Overlay';
|
import Overlay from './Overlay';
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
|
||||||
|
|||||||
@@ -50,6 +50,7 @@
|
|||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
|
|
||||||
> * + * {
|
> * + * {
|
||||||
|
@include test();
|
||||||
margin-top: $interiorMargin;
|
margin-top: $interiorMargin;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@
|
|||||||
|
|
||||||
.c-overlay {
|
.c-overlay {
|
||||||
@include abs();
|
@include abs();
|
||||||
z-index: 70;
|
z-index: 100;
|
||||||
|
|
||||||
&__blocker {
|
&__blocker {
|
||||||
display: none; // Mobile-first
|
display: none; // Mobile-first
|
||||||
|
|||||||
@@ -124,22 +124,6 @@ define([
|
|||||||
return sortedMetadata;
|
return sortedMetadata;
|
||||||
};
|
};
|
||||||
|
|
||||||
TelemetryMetadataManager.prototype.getDefaultDisplayValue = function () {
|
|
||||||
let valueMetadata = this.valuesForHints(['range'])[0];
|
|
||||||
|
|
||||||
if (valueMetadata === undefined) {
|
|
||||||
valueMetadata = this.values().filter(values => {
|
|
||||||
return !(values.hints.domain);
|
|
||||||
})[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (valueMetadata === undefined) {
|
|
||||||
valueMetadata = this.values()[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
return valueMetadata.key;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
return TelemetryMetadataManager;
|
return TelemetryMetadataManager;
|
||||||
|
|
||||||
|
|||||||
@@ -1,67 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* Open MCT, Copyright (c) 2014-2018, United States Government
|
|
||||||
* as represented by the Administrator of the National Aeronautics and Space
|
|
||||||
* Administration. All rights reserved.
|
|
||||||
*
|
|
||||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
|
||||||
* "License"); you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
* License for the specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
*
|
|
||||||
* Open MCT includes source code licensed under additional open source
|
|
||||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
|
||||||
* this source code distribution or the Licensing information page available
|
|
||||||
* at runtime from the About dialog for additional information.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
define([
|
|
||||||
'./components/LadTable.vue',
|
|
||||||
'vue'
|
|
||||||
], function (
|
|
||||||
LadTableComponent,
|
|
||||||
Vue
|
|
||||||
) {
|
|
||||||
function LADTableViewProvider(openmct) {
|
|
||||||
return {
|
|
||||||
key: 'LadTable',
|
|
||||||
name: 'LAD Table',
|
|
||||||
cssClass: 'icon-tabular-lad',
|
|
||||||
canView: function (domainObject) {
|
|
||||||
return domainObject.type === 'LadTable';
|
|
||||||
},
|
|
||||||
view: function (domainObject) {
|
|
||||||
let component;
|
|
||||||
|
|
||||||
return {
|
|
||||||
show: function (element) {
|
|
||||||
component = new Vue({
|
|
||||||
components: {
|
|
||||||
LadTableComponent: LadTableComponent.default
|
|
||||||
},
|
|
||||||
provide: {
|
|
||||||
openmct,
|
|
||||||
domainObject
|
|
||||||
},
|
|
||||||
el: element,
|
|
||||||
template: '<lad-table-component></lad-table-component>'
|
|
||||||
});
|
|
||||||
},
|
|
||||||
destroy: function (element) {
|
|
||||||
component.$destroy();
|
|
||||||
component = undefined;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
priority: function () {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return LADTableViewProvider;
|
|
||||||
});
|
|
||||||
@@ -1,120 +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>
|
|
||||||
<tr>
|
|
||||||
<td>{{name}}</td>
|
|
||||||
<td>{{timestamp}}</td>
|
|
||||||
<td :class="valueClass">
|
|
||||||
{{value}}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
inject: ['openmct'],
|
|
||||||
props: ['domainObject'],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
name: this.domainObject.name,
|
|
||||||
timestamp: '---',
|
|
||||||
value: '---',
|
|
||||||
valueClass: ''
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
updateValues(datum) {
|
|
||||||
this.timestamp = this.formats[this.timestampKey].format(datum);
|
|
||||||
this.value = this.formats[this.valueKey].format(datum);
|
|
||||||
|
|
||||||
var limit = this.limitEvaluator.evaluate(datum, this.valueMetadata);
|
|
||||||
|
|
||||||
if (limit) {
|
|
||||||
this.valueClass = limit.cssClass;
|
|
||||||
} else {
|
|
||||||
this.valueClass = '';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
updateName(name){
|
|
||||||
this.name = name;
|
|
||||||
},
|
|
||||||
updateTimeSystem(timeSystem) {
|
|
||||||
this.value = '---';
|
|
||||||
this.timestamp = '---';
|
|
||||||
this.valueClass = '';
|
|
||||||
this.timestampKey = timeSystem.key;
|
|
||||||
|
|
||||||
this.openmct
|
|
||||||
.telemetry
|
|
||||||
.request(this.domainObject, {strategy: 'latest'})
|
|
||||||
.then((array) => this.updateValues(array[array.length - 1]));
|
|
||||||
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.metadata = this.openmct.telemetry.getMetadata(this.domainObject);
|
|
||||||
this.formats = this.openmct.telemetry.getFormatMap(this.metadata);
|
|
||||||
|
|
||||||
this.limitEvaluator = openmct
|
|
||||||
.telemetry
|
|
||||||
.limitEvaluator(this.domainObject);
|
|
||||||
|
|
||||||
this.stopWatchingMutation = openmct
|
|
||||||
.objects
|
|
||||||
.observe(
|
|
||||||
this.domainObject,
|
|
||||||
'*',
|
|
||||||
this.updateName
|
|
||||||
);
|
|
||||||
|
|
||||||
this.openmct.time.on('timeSystem', this.updateTimeSystem);
|
|
||||||
|
|
||||||
this.timestampKey = this.openmct.time.timeSystem().key;
|
|
||||||
|
|
||||||
this.valueMetadata = this
|
|
||||||
.metadata
|
|
||||||
.valuesForHints(['range'])[0];
|
|
||||||
|
|
||||||
this.valueKey = this.valueMetadata.key
|
|
||||||
|
|
||||||
this.unsubscribe = this.openmct
|
|
||||||
.telemetry
|
|
||||||
.subscribe(this.domainObject, this.updateValues);
|
|
||||||
|
|
||||||
this.openmct
|
|
||||||
.telemetry
|
|
||||||
.request(this.domainObject, {strategy: 'latest'})
|
|
||||||
.then((array) => this.updateValues(array[array.length - 1]));
|
|
||||||
},
|
|
||||||
destroyed() {
|
|
||||||
this.stopWatchingMutation();
|
|
||||||
this.unsubscribe();
|
|
||||||
this.openmct.off('timeSystem', this.updateTimeSystem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
@@ -1,82 +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>
|
|
||||||
<table class="c-table c-lad-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Name</th>
|
|
||||||
<th>Timestamp</th>
|
|
||||||
<th>Value</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<lad-row
|
|
||||||
v-for="item in items"
|
|
||||||
:key="item.key"
|
|
||||||
:domainObject="item.domainObject">
|
|
||||||
</lad-row>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import lodash from 'lodash';
|
|
||||||
import LadRow from './LadRow.vue';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
inject: ['openmct', 'domainObject'],
|
|
||||||
components: {
|
|
||||||
LadRow
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
items: []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
addItem(domainObject) {
|
|
||||||
let item = {};
|
|
||||||
item.domainObject = domainObject;
|
|
||||||
item.key = this.openmct.objects.makeKeyString(domainObject.identifier);
|
|
||||||
|
|
||||||
this.items.push(item);
|
|
||||||
},
|
|
||||||
removeItem(identifier) {
|
|
||||||
let index = _.findIndex(this.items, (item) => this.openmct.objects.makeKeyString(identifier) === item.key);
|
|
||||||
|
|
||||||
this.items.splice(index, 1);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.composition = this.openmct.composition.get(this.domainObject);
|
|
||||||
this.composition.on('add', this.addItem);
|
|
||||||
this.composition.on('remove', this.removeItem);
|
|
||||||
this.composition.load();
|
|
||||||
},
|
|
||||||
destroyed() {
|
|
||||||
this.composition.off('add', this.addItem);
|
|
||||||
this.composition.off('remove', this.removeItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
@@ -1,135 +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>
|
|
||||||
<table class="c-table c-lad-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Name</th>
|
|
||||||
<th>Timestamp</th>
|
|
||||||
<th>Value</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<template
|
|
||||||
v-for="primary in primaryTelemetryObjects">
|
|
||||||
<tr class="c-table__group-header"
|
|
||||||
:key="primary.key">
|
|
||||||
<td colspan="10">{{primary.domainObject.name}}</td>
|
|
||||||
</tr>
|
|
||||||
<lad-row
|
|
||||||
v-for="secondary in secondaryTelemetryObjects[primary.key]"
|
|
||||||
:key="secondary.key"
|
|
||||||
:domainObject="secondary.domainObject">
|
|
||||||
</lad-row>
|
|
||||||
</template>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import lodash from 'lodash';
|
|
||||||
import LadRow from './LadRow.vue';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
inject: ['openmct', 'domainObject'],
|
|
||||||
components: {
|
|
||||||
LadRow
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
primaryTelemetryObjects: [],
|
|
||||||
secondaryTelemetryObjects: {},
|
|
||||||
compositions: []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
addPrimary(domainObject) {
|
|
||||||
let primary = {};
|
|
||||||
primary.domainObject = domainObject;
|
|
||||||
primary.key = this.openmct.objects.makeKeyString(domainObject.identifier);
|
|
||||||
|
|
||||||
this.$set(this.secondaryTelemetryObjects, primary.key, []);
|
|
||||||
this.primaryTelemetryObjects.push(primary);
|
|
||||||
|
|
||||||
let composition = openmct.composition.get(primary.domainObject),
|
|
||||||
addCallback = this.addSecondary(primary),
|
|
||||||
removeCallback = this.removeSecondary(primary);
|
|
||||||
|
|
||||||
composition.on('add', addCallback);
|
|
||||||
composition.on('remove', removeCallback);
|
|
||||||
composition.load();
|
|
||||||
|
|
||||||
this.compositions.push({composition, addCallback, removeCallback});
|
|
||||||
},
|
|
||||||
removePrimary(identifier) {
|
|
||||||
let index = _.findIndex(this.primaryTelemetryObjects, (primary) => this.openmct.objects.makeKeyString(identifier) === primary.key),
|
|
||||||
primary = this.primaryTelemetryObjects[index];
|
|
||||||
|
|
||||||
this.$set(this.secondaryTelemetryObjects, primary.key, undefined);
|
|
||||||
this.primaryTelemetryObjects.splice(index,1);
|
|
||||||
primary = undefined;
|
|
||||||
},
|
|
||||||
addSecondary(primary) {
|
|
||||||
return (domainObject) => {
|
|
||||||
let secondary = {};
|
|
||||||
secondary.key = this.openmct.objects.makeKeyString(domainObject.identifier);
|
|
||||||
secondary.domainObject = domainObject;
|
|
||||||
|
|
||||||
let array = this.secondaryTelemetryObjects[primary.key];
|
|
||||||
array.push(secondary);
|
|
||||||
|
|
||||||
this.$set(this.secondaryTelemetryObjects, primary.key, array);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
removeSecondary(primary) {
|
|
||||||
return (identifier) => {
|
|
||||||
let array = this.secondaryTelemetryObjects[primary.key],
|
|
||||||
index = _.findIndex(array, (secondary) => this.openmct.objects.makeKeyString(identifier) === secondary.key);
|
|
||||||
|
|
||||||
array.splice(index, 1);
|
|
||||||
|
|
||||||
this.$set(this.secondaryTelemetryObjects, primary.key, array);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.composition = this.openmct.composition.get(this.domainObject);
|
|
||||||
this.composition.on('add', this.addPrimary);
|
|
||||||
this.composition.on('remove', this.removePrimary);
|
|
||||||
this.composition.load();
|
|
||||||
},
|
|
||||||
destroyed() {
|
|
||||||
this.composition.off('add', this.addPrimary);
|
|
||||||
this.composition.off('remove', this.removePrimary);
|
|
||||||
this.compositions.forEach(c => {
|
|
||||||
c.composition.off('add', c.addCallback);
|
|
||||||
c.composition.off('remove', c.removeCallback);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* Open MCT, Copyright (c) 2014-2018, United States Government
|
|
||||||
* as represented by the Administrator of the National Aeronautics and Space
|
|
||||||
* Administration. All rights reserved.
|
|
||||||
*
|
|
||||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
|
||||||
* "License"); you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
* License for the specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
*
|
|
||||||
* Open MCT includes source code licensed under additional open source
|
|
||||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
|
||||||
* this source code distribution or the Licensing information page available
|
|
||||||
* at runtime from the About dialog for additional information.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
define([
|
|
||||||
'./LADTableViewProvider',
|
|
||||||
'./LADTableSetViewProvider'
|
|
||||||
], function (
|
|
||||||
LADTableViewProvider,
|
|
||||||
LADTableSetViewProvider
|
|
||||||
) {
|
|
||||||
return function plugin() {
|
|
||||||
return function install(openmct) {
|
|
||||||
openmct.objectViews.addProvider(new LADTableViewProvider(openmct));
|
|
||||||
openmct.objectViews.addProvider(new LADTableSetViewProvider(openmct));
|
|
||||||
|
|
||||||
openmct.types.addType('LadTable', {
|
|
||||||
name: "LAD Table",
|
|
||||||
creatable: true,
|
|
||||||
description: "A Latest Available Data tabular view in which each row displays the values for one or more contained telemetry objects.",
|
|
||||||
cssClass: 'icon-tabular-lad',
|
|
||||||
initialize(domainObject) {
|
|
||||||
domainObject.composition = [];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
openmct.types.addType('LadTableSet', {
|
|
||||||
name: "LAD Table Set",
|
|
||||||
creatable: true,
|
|
||||||
description: "A Latest Available Data tabular view in which each row displays the values for one or more contained telemetry objects.",
|
|
||||||
cssClass: 'icon-tabular-lad-set',
|
|
||||||
initialize(domainObject) {
|
|
||||||
domainObject.composition = [];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
};
|
|
||||||
});
|
|
||||||
@@ -32,51 +32,9 @@ define([], function () {
|
|||||||
// is inside a layout, or the main layout is selected.
|
// is inside a layout, or the main layout is selected.
|
||||||
return (openmct.editor.isEditing() && selection &&
|
return (openmct.editor.isEditing() && selection &&
|
||||||
((selection[1] && selection[1].context.item && selection[1].context.item.type === 'layout') ||
|
((selection[1] && selection[1].context.item && selection[1].context.item.type === 'layout') ||
|
||||||
(selection[0].context.item && selection[0].context.item.type === 'layout')));
|
(selection[0].context.item && selection[0].context.item.type === 'layout')));
|
||||||
},
|
},
|
||||||
toolbar: function (selection) {
|
toolbar: function (selection) {
|
||||||
const DIALOG_FORM = {
|
|
||||||
'text': {
|
|
||||||
name: "Text Element Properties",
|
|
||||||
sections: [
|
|
||||||
{
|
|
||||||
rows: [
|
|
||||||
{
|
|
||||||
key: "text",
|
|
||||||
control: "textfield",
|
|
||||||
name: "Text",
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
'image': {
|
|
||||||
name: "Image Properties",
|
|
||||||
sections: [
|
|
||||||
{
|
|
||||||
rows: [
|
|
||||||
{
|
|
||||||
key: "url",
|
|
||||||
control: "textfield",
|
|
||||||
name: "Image URL",
|
|
||||||
"cssClass": "l-input-lg",
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function getUserInput(form) {
|
|
||||||
return openmct.$injector.get('dialogService').getUserInput(form, {});
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPath() {
|
|
||||||
return `configuration.items[${selection[0].context.index}]`;
|
|
||||||
}
|
|
||||||
|
|
||||||
let selectedParent = selection[1] && selection[1].context.item,
|
let selectedParent = selection[1] && selection[1].context.item,
|
||||||
selectedObject = selection[0].context.item,
|
selectedObject = selection[0].context.item,
|
||||||
layoutItem = selection[0].context.layoutItem,
|
layoutItem = selection[0].context.layoutItem,
|
||||||
@@ -87,14 +45,7 @@ define([], function () {
|
|||||||
control: "menu",
|
control: "menu",
|
||||||
domainObject: selectedObject,
|
domainObject: selectedObject,
|
||||||
method: function (option) {
|
method: function (option) {
|
||||||
let name = option.name.toLowerCase();
|
selection[0].context.addElement(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",
|
key: "add",
|
||||||
icon: "icon-plus",
|
icon: "icon-plus",
|
||||||
@@ -124,244 +75,138 @@ define([], function () {
|
|||||||
return toolbar;
|
return toolbar;
|
||||||
}
|
}
|
||||||
|
|
||||||
let separator = {
|
let path = layoutItem.config.path();
|
||||||
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 (layoutItem.type === 'subobject-view') {
|
||||||
if (toolbar.length > 0) {
|
if (toolbar.length > 0) {
|
||||||
toolbar.push(separator);
|
toolbar.push({
|
||||||
|
control: "separator"
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
toolbar.push({
|
toolbar.push({
|
||||||
control: "toggle-button",
|
control: "toggle-button",
|
||||||
domainObject: selectedParent,
|
domainObject: selectedParent,
|
||||||
property: function () {
|
property: path + ".hasFrame",
|
||||||
return getPath() + ".hasFrame";
|
|
||||||
},
|
|
||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
value: false,
|
value: false,
|
||||||
icon: 'icon-frame-show',
|
icon: 'icon-frame-hide',
|
||||||
title: "Frame visible"
|
title: "Hide frame"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: true,
|
value: true,
|
||||||
icon: 'icon-frame-hide',
|
icon: 'icon-frame-show',
|
||||||
title: "Frame hidden"
|
title: "Show frame"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
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 {
|
} else {
|
||||||
const TEXT_SIZE = [8, 9, 10, 11, 12, 13, 14, 15, 16, 20, 24, 30, 36, 48, 72, 96, 128];
|
const TEXT_SIZE = [9, 10, 11, 12, 13, 14, 15, 16, 20, 24, 30, 36, 48, 72, 96];
|
||||||
let fill = {
|
let separator = {
|
||||||
control: "color-picker",
|
control: "separator"
|
||||||
domainObject: selectedParent,
|
|
||||||
property: function () {
|
|
||||||
return getPath() + ".fill";
|
|
||||||
},
|
},
|
||||||
icon: "icon-paint-bucket",
|
fill = {
|
||||||
title: "Set fill color"
|
control: "color-picker",
|
||||||
},
|
domainObject: selectedParent,
|
||||||
stroke = {
|
property: path + ".fill",
|
||||||
control: "color-picker",
|
icon: "icon-paint-bucket",
|
||||||
domainObject: selectedParent,
|
title: "Set fill color"
|
||||||
property: function () {
|
|
||||||
return getPath() + ".stroke";
|
|
||||||
},
|
},
|
||||||
icon: "icon-line-horz",
|
stroke = {
|
||||||
title: "Set border color"
|
control: "color-picker",
|
||||||
},
|
domainObject: selectedParent,
|
||||||
color = {
|
property: path + ".stroke",
|
||||||
control: "color-picker",
|
icon: "icon-line-horz",
|
||||||
domainObject: selectedParent,
|
title: "Set border color"
|
||||||
property: function () {
|
|
||||||
return getPath() + ".color";
|
|
||||||
},
|
},
|
||||||
icon: "icon-font",
|
color = {
|
||||||
mandatory: true,
|
control: "color-picker",
|
||||||
title: "Set text color",
|
domainObject: selectedParent,
|
||||||
preventNone: true
|
property: path + ".color",
|
||||||
},
|
icon: "icon-font",
|
||||||
size = {
|
mandatory: true,
|
||||||
control: "select-menu",
|
title: "Set text color",
|
||||||
domainObject: selectedParent,
|
preventNone: true
|
||||||
property: function () {
|
|
||||||
return getPath() + ".size";
|
|
||||||
},
|
},
|
||||||
title: "Set text size",
|
size = {
|
||||||
options: TEXT_SIZE.map(size => {
|
control: "select-menu",
|
||||||
return {
|
domainObject: selectedParent,
|
||||||
value: size + "px"
|
property: path + ".size",
|
||||||
};
|
title: "Set text size",
|
||||||
})
|
options: TEXT_SIZE.map(size => {
|
||||||
};
|
return {
|
||||||
|
value: size + "px"
|
||||||
|
};
|
||||||
|
})
|
||||||
|
},
|
||||||
|
x = {
|
||||||
|
control: "input",
|
||||||
|
type: "number",
|
||||||
|
domainObject: selectedParent,
|
||||||
|
property: path + ".x",
|
||||||
|
label: "X:",
|
||||||
|
title: "X position"
|
||||||
|
},
|
||||||
|
y = {
|
||||||
|
control: "input",
|
||||||
|
type: "number",
|
||||||
|
domainObject: selectedParent,
|
||||||
|
property: path + ".y",
|
||||||
|
label: "Y:",
|
||||||
|
title: "Y position",
|
||||||
|
},
|
||||||
|
width = {
|
||||||
|
control: 'input',
|
||||||
|
type: 'number',
|
||||||
|
domainObject: selectedParent,
|
||||||
|
property: path + ".width",
|
||||||
|
label: 'W:',
|
||||||
|
title: 'Resize object width'
|
||||||
|
},
|
||||||
|
height = {
|
||||||
|
control: 'input',
|
||||||
|
type: 'number',
|
||||||
|
domainObject: selectedParent,
|
||||||
|
property: path + ".height",
|
||||||
|
label: 'H:',
|
||||||
|
title: 'Resize object height'
|
||||||
|
};
|
||||||
|
|
||||||
if (layoutItem.type === 'telemetry-view') {
|
if (layoutItem.type === 'telemetry-view') {
|
||||||
let displayMode = {
|
// TODO: add "remove", "order", "useGrid"
|
||||||
control: "select-menu",
|
let metadata = openmct.telemetry.getMetadata(layoutItem.domainObject),
|
||||||
domainObject: selectedParent,
|
displayMode = {
|
||||||
property: function () {
|
control: "select-menu",
|
||||||
return getPath() + ".displayMode";
|
domainObject: selectedParent,
|
||||||
|
property: path + ".displayMode",
|
||||||
|
title: "Set display mode",
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Label + Value',
|
||||||
|
value: 'all'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Label only",
|
||||||
|
value: "label"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Value only",
|
||||||
|
value: "value"
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
title: "Set display mode",
|
value = {
|
||||||
options: [
|
control: "select-menu",
|
||||||
{
|
domainObject: selectedParent,
|
||||||
name: 'Label + Value',
|
property: path + ".value",
|
||||||
value: 'all'
|
title: "Set value",
|
||||||
},
|
options: metadata.values().map(value => {
|
||||||
{
|
return {
|
||||||
name: "Label only",
|
name: value.name,
|
||||||
value: "label"
|
value: value.key
|
||||||
},
|
}
|
||||||
{
|
})
|
||||||
name: "Value only",
|
};
|
||||||
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
|
|
||||||
}
|
|
||||||
})
|
|
||||||
};
|
|
||||||
toolbar = [
|
toolbar = [
|
||||||
displayMode,
|
displayMode,
|
||||||
separator,
|
separator,
|
||||||
@@ -373,116 +218,98 @@ define([], function () {
|
|||||||
separator,
|
separator,
|
||||||
size,
|
size,
|
||||||
separator,
|
separator,
|
||||||
stackOrder,
|
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
height,
|
height,
|
||||||
width,
|
width
|
||||||
useGrid,
|
|
||||||
separator,
|
|
||||||
remove
|
|
||||||
];
|
];
|
||||||
} else if (layoutItem.type === 'text-view') {
|
} else if (layoutItem.type === 'text-view' ) {
|
||||||
|
// TODO: Add "remove", "order", "useGrid"
|
||||||
let text = {
|
let text = {
|
||||||
control: "button",
|
control: "button",
|
||||||
domainObject: selectedParent,
|
domainObject: selectedParent,
|
||||||
property: function () {
|
property: path,
|
||||||
return getPath();
|
|
||||||
},
|
|
||||||
icon: "icon-gear",
|
icon: "icon-gear",
|
||||||
title: "Edit text properties",
|
title: "Edit text properties",
|
||||||
dialog: DIALOG_FORM['text']
|
dialog: {
|
||||||
|
name: "Text Element Properties",
|
||||||
|
sections: [
|
||||||
|
{
|
||||||
|
rows: [
|
||||||
|
{
|
||||||
|
key: "text",
|
||||||
|
control: "textfield",
|
||||||
|
name: "Text",
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
};
|
};
|
||||||
toolbar = [
|
toolbar = [
|
||||||
fill,
|
fill,
|
||||||
stroke,
|
stroke,
|
||||||
separator,
|
|
||||||
color,
|
color,
|
||||||
|
separator,
|
||||||
size,
|
size,
|
||||||
separator,
|
separator,
|
||||||
stackOrder,
|
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
height,
|
height,
|
||||||
width,
|
width,
|
||||||
useGrid,
|
|
||||||
separator,
|
separator,
|
||||||
text,
|
text
|
||||||
separator,
|
|
||||||
remove
|
|
||||||
];
|
];
|
||||||
} else if (layoutItem.type === 'box-view') {
|
} else if (layoutItem.type === 'box-view') {
|
||||||
|
// TODO: Add "remove", "order", "useGrid"
|
||||||
toolbar = [
|
toolbar = [
|
||||||
fill,
|
fill,
|
||||||
stroke,
|
stroke,
|
||||||
separator,
|
separator,
|
||||||
stackOrder,
|
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
height,
|
height,
|
||||||
width,
|
width
|
||||||
useGrid,
|
|
||||||
separator,
|
|
||||||
remove
|
|
||||||
];
|
];
|
||||||
} else if (layoutItem.type === 'image-view') {
|
} else if (layoutItem.type === 'image-view') {
|
||||||
|
// TODO: Add "remove", "order", "useGrid"
|
||||||
let url = {
|
let url = {
|
||||||
control: "button",
|
control: "button",
|
||||||
domainObject: selectedParent,
|
domainObject: selectedParent,
|
||||||
property: function () {
|
property: path,
|
||||||
return getPath();
|
|
||||||
},
|
|
||||||
icon: "icon-image",
|
icon: "icon-image",
|
||||||
title: "Edit image properties",
|
title: "Edit image properties",
|
||||||
dialog: DIALOG_FORM['image']
|
dialog: {
|
||||||
};
|
name: "Image Properties",
|
||||||
|
sections: [
|
||||||
|
{
|
||||||
|
rows: [
|
||||||
|
{
|
||||||
|
key: "url",
|
||||||
|
control: "textfield",
|
||||||
|
name: "Image URL",
|
||||||
|
"cssClass": "l-input-lg",
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
toolbar = [
|
toolbar = [
|
||||||
stroke,
|
stroke,
|
||||||
separator,
|
separator,
|
||||||
stackOrder,
|
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
height,
|
height,
|
||||||
width,
|
width,
|
||||||
useGrid,
|
|
||||||
separator,
|
separator,
|
||||||
url,
|
url
|
||||||
separator,
|
|
||||||
remove
|
|
||||||
];
|
];
|
||||||
} else if (layoutItem.type === 'line-view') {
|
} else if (layoutItem.type === 'line-view') {
|
||||||
let x2 = {
|
// TODO: Add "remove", "order", "useGrid", "x1", "y1", x2", "y2"
|
||||||
control: "input",
|
toolbar = [stroke];
|
||||||
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
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,17 +25,15 @@ define(function () {
|
|||||||
return {
|
return {
|
||||||
name: "Display Layout",
|
name: "Display Layout",
|
||||||
creatable: true,
|
creatable: true,
|
||||||
description: 'Assemble other objects and components together into a reusable screen layout. Simply drag in the objects you want, position and size them. Save your design and view or edit it at any time.',
|
|
||||||
cssClass: 'icon-layout',
|
cssClass: 'icon-layout',
|
||||||
initialize(domainObject) {
|
initialize(domainObject) {
|
||||||
domainObject.composition = [];
|
domainObject.composition = [];
|
||||||
domainObject.configuration = {
|
domainObject.configuration = {
|
||||||
items: [],
|
items: []
|
||||||
layoutGrid: [10, 10],
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return DisplayLayoutType;
|
return DisplayLayoutType;
|
||||||
});
|
});
|
||||||
171
src/plugins/displayLayout/ElementViewConfiguration.js
Normal file
171
src/plugins/displayLayout/ElementViewConfiguration.js
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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(
|
||||||
|
['./ViewConfiguration'],
|
||||||
|
function (ViewConfiguration) {
|
||||||
|
class ElementViewConfiguration extends ViewConfiguration {
|
||||||
|
|
||||||
|
static create(type, openmct) {
|
||||||
|
const DEFAULT_WIDTH = 10,
|
||||||
|
DEFAULT_HEIGHT = 5,
|
||||||
|
DEFAULT_X = 1,
|
||||||
|
DEFAULT_Y = 1;
|
||||||
|
const INITIAL_STATES = {
|
||||||
|
"image": {
|
||||||
|
stroke: "transparent"
|
||||||
|
},
|
||||||
|
"box": {
|
||||||
|
fill: "#717171",
|
||||||
|
stroke: "transparent"
|
||||||
|
},
|
||||||
|
"line": {
|
||||||
|
x: 5,
|
||||||
|
y: 3,
|
||||||
|
x2: 6,
|
||||||
|
y2: 6,
|
||||||
|
stroke: "#717171"
|
||||||
|
},
|
||||||
|
"text": {
|
||||||
|
fill: "transparent",
|
||||||
|
stroke: "transparent",
|
||||||
|
size: "13px",
|
||||||
|
color: ""
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const DIALOGS = {
|
||||||
|
"image": {
|
||||||
|
name: "Image Properties",
|
||||||
|
sections: [
|
||||||
|
{
|
||||||
|
rows: [
|
||||||
|
{
|
||||||
|
key: "url",
|
||||||
|
control: "textfield",
|
||||||
|
name: "Image URL",
|
||||||
|
"cssClass": "l-input-lg",
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"text": {
|
||||||
|
name: "Text Element Properties",
|
||||||
|
sections: [
|
||||||
|
{
|
||||||
|
rows: [
|
||||||
|
{
|
||||||
|
key: "text",
|
||||||
|
control: "textfield",
|
||||||
|
name: "Text",
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let element = INITIAL_STATES[type] || {};
|
||||||
|
element = JSON.parse(JSON.stringify(element));
|
||||||
|
element.x = element.x || DEFAULT_X;
|
||||||
|
element.y = element.y || DEFAULT_Y;
|
||||||
|
element.width = DEFAULT_WIDTH;
|
||||||
|
element.height = DEFAULT_HEIGHT;
|
||||||
|
element.type = type;
|
||||||
|
|
||||||
|
return DIALOGS[type] ?
|
||||||
|
openmct.$injector.get('dialogService').getUserInput(DIALOGS[type], element) :
|
||||||
|
element;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Object} configuration the element (line, box, text or image) view configuration
|
||||||
|
* @param {Object} configuration.element
|
||||||
|
* @param {Object} configuration.domainObject the telemetry domain object
|
||||||
|
* @param {Object} configuration.openmct the openmct object
|
||||||
|
*/
|
||||||
|
constructor({element, ...rest}) {
|
||||||
|
super(rest);
|
||||||
|
this.element = element;
|
||||||
|
this.updateStyle(this.position());
|
||||||
|
}
|
||||||
|
|
||||||
|
path() {
|
||||||
|
return "configuration.items[" + this.element.index + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
x() {
|
||||||
|
return this.element.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
y() {
|
||||||
|
return this.element.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
width() {
|
||||||
|
return this.element.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
height() {
|
||||||
|
return this.element.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
observeProperties() {
|
||||||
|
[
|
||||||
|
"width",
|
||||||
|
"height",
|
||||||
|
"stroke",
|
||||||
|
"fill",
|
||||||
|
"x",
|
||||||
|
"y",
|
||||||
|
"x1",
|
||||||
|
"y1",
|
||||||
|
"x2",
|
||||||
|
"y2",
|
||||||
|
"color",
|
||||||
|
"size",
|
||||||
|
"text",
|
||||||
|
"url"
|
||||||
|
].forEach(property => {
|
||||||
|
this.attachListener(property, newValue => {
|
||||||
|
this.element[property] = newValue;
|
||||||
|
|
||||||
|
if (property === 'width' || property === 'height' ||
|
||||||
|
property === 'x' || property === 'y') {
|
||||||
|
this.updateStyle();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: attach listener for useGrid
|
||||||
|
}
|
||||||
|
|
||||||
|
inspectable() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ElementViewConfiguration;
|
||||||
|
}
|
||||||
|
);
|
||||||
122
src/plugins/displayLayout/SubobjectViewConfiguration.js
Normal file
122
src/plugins/displayLayout/SubobjectViewConfiguration.js
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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(
|
||||||
|
['./ViewConfiguration'],
|
||||||
|
function (ViewConfiguration) {
|
||||||
|
class SubobjectViewConfiguration extends ViewConfiguration {
|
||||||
|
|
||||||
|
static create(domainObject, gridSize, position) {
|
||||||
|
const MINIMUM_FRAME_SIZE = [320, 180],
|
||||||
|
DEFAULT_DIMENSIONS = [10, 10],
|
||||||
|
DEFAULT_POSITION = [0, 0],
|
||||||
|
DEFAULT_HIDDEN_FRAME_TYPES = ['hyperlink', 'summary-widget'];
|
||||||
|
|
||||||
|
function getDefaultDimensions() {
|
||||||
|
return MINIMUM_FRAME_SIZE.map((min, index) => {
|
||||||
|
return Math.max(
|
||||||
|
Math.ceil(min / gridSize[index]),
|
||||||
|
DEFAULT_DIMENSIONS[index]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasFrameByDefault(type) {
|
||||||
|
return DEFAULT_HIDDEN_FRAME_TYPES.indexOf(type) === -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
position = position || DEFAULT_POSITION;
|
||||||
|
let defaultDimensions = getDefaultDimensions();
|
||||||
|
let panel = {
|
||||||
|
identifier: domainObject.identifier,
|
||||||
|
type: 'subobject',
|
||||||
|
width: defaultDimensions[0],
|
||||||
|
height: defaultDimensions[1],
|
||||||
|
x: position[0],
|
||||||
|
y: position[1],
|
||||||
|
hasFrame: hasFrameByDefault(domainObject.type)
|
||||||
|
};
|
||||||
|
|
||||||
|
return panel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Object} configuration the subobject view configuration
|
||||||
|
* @param {Boolean} configuration.panel
|
||||||
|
* @param {Object} configuration.domainObject the domain object to observe the changes on
|
||||||
|
* @param {Object} configuration.openmct the openmct object
|
||||||
|
*/
|
||||||
|
constructor({panel, ...rest}) {
|
||||||
|
super(rest);
|
||||||
|
this.panel = panel;
|
||||||
|
this.hasFrame = this.hasFrame.bind(this);
|
||||||
|
this.updateStyle(this.position());
|
||||||
|
}
|
||||||
|
|
||||||
|
path() {
|
||||||
|
return "configuration.items[" + this.panel.index + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
x() {
|
||||||
|
return this.panel.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
y() {
|
||||||
|
return this.panel.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
width() {
|
||||||
|
return this.panel.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
height() {
|
||||||
|
return this.panel.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasFrame() {
|
||||||
|
return this.panel.hasFrame;
|
||||||
|
}
|
||||||
|
|
||||||
|
observeProperties() {
|
||||||
|
[
|
||||||
|
'hasFrame',
|
||||||
|
'x',
|
||||||
|
'y',
|
||||||
|
'width',
|
||||||
|
'height'
|
||||||
|
].forEach(property => {
|
||||||
|
this.attachListener(property, newValue => {
|
||||||
|
this.panel[property] = newValue;
|
||||||
|
|
||||||
|
if (property === 'width' || property === 'height' ||
|
||||||
|
property === 'x' || property === 'y') {
|
||||||
|
this.updateStyle();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return SubobjectViewConfiguration;
|
||||||
|
}
|
||||||
|
);
|
||||||
128
src/plugins/displayLayout/TelemetryViewConfiguration.js
Normal file
128
src/plugins/displayLayout/TelemetryViewConfiguration.js
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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(
|
||||||
|
['./ViewConfiguration'],
|
||||||
|
function (ViewConfiguration) {
|
||||||
|
|
||||||
|
class TelemetryViewConfiguration extends ViewConfiguration {
|
||||||
|
static create(domainObject, position, openmct) {
|
||||||
|
const DEFAULT_TELEMETRY_DIMENSIONS = [10, 5],
|
||||||
|
DEFAULT_POSITION = [1, 1];
|
||||||
|
|
||||||
|
function getDefaultTelemetryValue(domainObject, openmct) {
|
||||||
|
let metadata = openmct.telemetry.getMetadata(domainObject);
|
||||||
|
let valueMetadata = metadata.valuesForHints(['range'])[0];
|
||||||
|
|
||||||
|
if (valueMetadata === undefined) {
|
||||||
|
valueMetadata = metadata.values().filter(values => {
|
||||||
|
return !(values.hints.domain);
|
||||||
|
})[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valueMetadata === undefined) {
|
||||||
|
valueMetadata = metadata.values()[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return valueMetadata.key;
|
||||||
|
}
|
||||||
|
|
||||||
|
position = position || DEFAULT_POSITION;
|
||||||
|
let alphanumeric = {
|
||||||
|
identifier: domainObject.identifier,
|
||||||
|
x: position[0],
|
||||||
|
y: position[1],
|
||||||
|
width: DEFAULT_TELEMETRY_DIMENSIONS[0],
|
||||||
|
height: DEFAULT_TELEMETRY_DIMENSIONS[1],
|
||||||
|
displayMode: 'all',
|
||||||
|
value: getDefaultTelemetryValue(domainObject, openmct),
|
||||||
|
stroke: "transparent",
|
||||||
|
fill: "",
|
||||||
|
color: "",
|
||||||
|
size: "13px",
|
||||||
|
type: "telemetry"
|
||||||
|
};
|
||||||
|
|
||||||
|
return alphanumeric;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Object} configuration the telemetry object view configuration
|
||||||
|
* @param {Object} configuration.alphanumeric
|
||||||
|
* @param {Object} configuration.domainObject the domain object to observe the changes on
|
||||||
|
* @param {Object} configuration.openmct the openmct object
|
||||||
|
*/
|
||||||
|
constructor({alphanumeric, ...rest}) {
|
||||||
|
super(rest);
|
||||||
|
this.alphanumeric = alphanumeric;
|
||||||
|
this.updateStyle(this.position());
|
||||||
|
}
|
||||||
|
|
||||||
|
path() {
|
||||||
|
console.log("path", "configuration.items[" + this.alphanumeric.index + "]");
|
||||||
|
return "configuration.items[" + this.alphanumeric.index + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
x() {
|
||||||
|
return this.alphanumeric.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
y() {
|
||||||
|
return this.alphanumeric.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
width() {
|
||||||
|
return this.alphanumeric.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
height() {
|
||||||
|
return this.alphanumeric.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
observeProperties() {
|
||||||
|
[
|
||||||
|
'displayMode',
|
||||||
|
'value',
|
||||||
|
'fill',
|
||||||
|
'stroke',
|
||||||
|
'color',
|
||||||
|
'size',
|
||||||
|
'x',
|
||||||
|
'y',
|
||||||
|
'width',
|
||||||
|
'height'
|
||||||
|
].forEach(property => {
|
||||||
|
this.attachListener(property, newValue => {
|
||||||
|
this.alphanumeric[property] = newValue;
|
||||||
|
|
||||||
|
if (property === 'width' || property === 'height' ||
|
||||||
|
property === 'x' || property === 'y') {
|
||||||
|
this.updateStyle();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return TelemetryViewConfiguration;
|
||||||
|
}
|
||||||
|
);
|
||||||
121
src/plugins/displayLayout/ViewConfiguration.js
Normal file
121
src/plugins/displayLayout/ViewConfiguration.js
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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 () {
|
||||||
|
class ViewConfiguration {
|
||||||
|
|
||||||
|
constructor({domainObject, openmct, gridSize}) {
|
||||||
|
this.domainObject = domainObject;
|
||||||
|
this.gridSize = gridSize;
|
||||||
|
this.mutatePosition = this.mutatePosition.bind(this);
|
||||||
|
this.listeners = [];
|
||||||
|
this.observe = openmct.objects.observe.bind(openmct.objects);
|
||||||
|
this.mutate = function (path, value) {
|
||||||
|
openmct.objects.mutate(this.domainObject, path, value);
|
||||||
|
}.bind(this);
|
||||||
|
this.newPosition = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
mutatePosition() {
|
||||||
|
this.mutate(this.path() + ".x", this.newPosition.position[0]);
|
||||||
|
this.mutate(this.path() + ".y", this.newPosition.position[1]);
|
||||||
|
this.mutate(this.path() + ".width", this.newPosition.dimensions[0]);
|
||||||
|
this.mutate(this.path() + ".height", this.newPosition.dimensions[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
attachListener(property, callback) {
|
||||||
|
this.listeners.push(this.observe(this.domainObject, this.path() + "." + property, callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
attachListeners() {
|
||||||
|
this.observeProperties();
|
||||||
|
this.listeners.push(this.observe(this.domainObject, '*', function (obj) {
|
||||||
|
this.domainObject = JSON.parse(JSON.stringify(obj));
|
||||||
|
}.bind(this)));
|
||||||
|
}
|
||||||
|
|
||||||
|
removeListeners() {
|
||||||
|
this.listeners.forEach(listener => {
|
||||||
|
listener();
|
||||||
|
});
|
||||||
|
this.listeners = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
position() {
|
||||||
|
return {
|
||||||
|
position: [this.x(), this.y()],
|
||||||
|
dimensions: [this.width(), this.height()]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
path() {
|
||||||
|
throw "NOT IMPLEMENTED;"
|
||||||
|
}
|
||||||
|
|
||||||
|
inspectable() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateStyle(raw) {
|
||||||
|
if (!raw) {
|
||||||
|
raw = this.position();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.style = {
|
||||||
|
left: (this.gridSize[0] * raw.position[0]) + 'px',
|
||||||
|
top: (this.gridSize[1] * raw.position[1]) + 'px',
|
||||||
|
width: (this.gridSize[0] * raw.dimensions[0]) + 'px',
|
||||||
|
height: (this.gridSize[1] * raw.dimensions[1]) + 'px',
|
||||||
|
minWidth: (this.gridSize[0] * raw.dimensions[0]) + 'px',
|
||||||
|
minHeight: (this.gridSize[1] * raw.dimensions[1]) + 'px'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
observeProperties() {
|
||||||
|
// Not implemented
|
||||||
|
}
|
||||||
|
|
||||||
|
x() {
|
||||||
|
// Not implemented
|
||||||
|
}
|
||||||
|
|
||||||
|
y() {
|
||||||
|
// Not implemented
|
||||||
|
}
|
||||||
|
|
||||||
|
width() {
|
||||||
|
// Not implemented
|
||||||
|
}
|
||||||
|
|
||||||
|
height() {
|
||||||
|
// Not implemented
|
||||||
|
}
|
||||||
|
|
||||||
|
hasFrame() {
|
||||||
|
// Not implemented
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ViewConfiguration;
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -21,13 +21,9 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<layout-frame :item="item"
|
<div class="c-box-view"
|
||||||
:grid-size="gridSize"
|
:style="styleObject">
|
||||||
@endDrag="(item, updates) => $emit('endDrag', item, updates)">
|
</div>
|
||||||
<div class="c-box-view"
|
|
||||||
:style="style">
|
|
||||||
</div>
|
|
||||||
</layout-frame>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@@ -44,60 +40,24 @@
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import LayoutFrame from './LayoutFrame.vue'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
makeDefinition() {
|
|
||||||
return {
|
|
||||||
fill: '#717171',
|
|
||||||
stroke: 'transparent',
|
|
||||||
x: 1,
|
|
||||||
y: 1,
|
|
||||||
width: 10,
|
|
||||||
height: 5,
|
|
||||||
useGrid: true
|
|
||||||
};
|
|
||||||
},
|
|
||||||
inject: ['openmct'],
|
|
||||||
components: {
|
|
||||||
LayoutFrame
|
|
||||||
},
|
|
||||||
props: {
|
props: {
|
||||||
item: Object,
|
item: Object
|
||||||
gridSize: Array,
|
|
||||||
index: Number,
|
|
||||||
initSelect: Boolean
|
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
style() {
|
styleObject() {
|
||||||
|
let element = this.item.config.element;
|
||||||
return {
|
return {
|
||||||
backgroundColor: this.item.fill,
|
backgroundColor: element.fill,
|
||||||
border: '1px solid ' + this.item.stroke
|
border: '1px solid ' + element.stroke
|
||||||
};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
index(newIndex) {
|
|
||||||
if (!this.context) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.context.index = newIndex;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.context = {
|
this.item.config.attachListeners();
|
||||||
layoutItem: this.item,
|
|
||||||
index: this.index
|
|
||||||
};
|
|
||||||
this.removeSelectable = this.openmct.selection.selectable(
|
|
||||||
this.$el, this.context, this.initSelect);
|
|
||||||
},
|
},
|
||||||
destroyed() {
|
destroyed() {
|
||||||
if (this.removeSelectable) {
|
this.item.config.removeListeners();
|
||||||
this.removeSelectable();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -25,26 +25,28 @@
|
|||||||
@dragover="handleDragOver"
|
@dragover="handleDragOver"
|
||||||
@click="bypassSelection"
|
@click="bypassSelection"
|
||||||
@drop="handleDrop">
|
@drop="handleDrop">
|
||||||
<!-- Background grid -->
|
<div class="l-layout__object">
|
||||||
<div class="l-layout__grid-holder c-grid">
|
<!-- Background grid -->
|
||||||
<div class="c-grid__x l-grid l-grid-x"
|
<div class="l-layout__grid-holder c-grid"
|
||||||
v-if="gridSize[0] >= 3"
|
v-if="!drilledIn">
|
||||||
:style="[{ backgroundSize: gridSize[0] + 'px 100%' }]">
|
<div class="c-grid__x l-grid l-grid-x"
|
||||||
|
v-if="gridSize[0] >= 3"
|
||||||
|
:style="[{ backgroundSize: gridSize[0] + 'px 100%' }]">
|
||||||
|
</div>
|
||||||
|
<div class="c-grid__y l-grid l-grid-y"
|
||||||
|
v-if="gridSize[1] >= 3"
|
||||||
|
:style="[{ backgroundSize: '100%' + gridSize[1] + 'px' }]"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="c-grid__y l-grid l-grid-y"
|
<layout-item v-for="(item, index) in layoutItems"
|
||||||
v-if="gridSize[1] >= 3"
|
class="l-layout__frame"
|
||||||
:style="[{ backgroundSize: '100%' + gridSize[1] + 'px' }]"></div>
|
:key="index"
|
||||||
|
:item="item"
|
||||||
|
:gridSize="gridSize"
|
||||||
|
@drilledIn="updateDrilledInState"
|
||||||
|
@dragInProgress="updatePosition"
|
||||||
|
@endDrag="endDrag">
|
||||||
|
</layout-item>
|
||||||
</div>
|
</div>
|
||||||
<component v-for="(item, index) in layoutItems"
|
|
||||||
:is="item.type"
|
|
||||||
:item="item"
|
|
||||||
:key="item.id"
|
|
||||||
:gridSize="item.useGrid ? gridSize : [1, 1]"
|
|
||||||
:initSelect="initSelectIndex === index"
|
|
||||||
:index="index"
|
|
||||||
@endDrag="endDrag"
|
|
||||||
>
|
|
||||||
</component>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -55,156 +57,150 @@
|
|||||||
@include abs();
|
@include abs();
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow: auto;
|
|
||||||
|
|
||||||
&__grid-holder {
|
&__grid-holder {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__object {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
&__frame {
|
&__frame {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.is-editing {
|
.l-shell__main-container {
|
||||||
.l-shell__main-container {
|
> .l-layout {
|
||||||
&[s-selected],
|
[s-selected] {
|
||||||
&[s-selected-parent] {
|
border: $browseBorderSelected;
|
||||||
// Display grid in main layout holder when editing
|
|
||||||
> .l-layout {
|
|
||||||
background: $editUIGridColorBg;
|
|
||||||
|
|
||||||
> [class*="__grid-holder"] {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.l-layout__frame {
|
|
||||||
&[s-selected],
|
|
||||||
&[s-selected-parent] {
|
|
||||||
// Display grid in nested layouts when editing
|
|
||||||
> * > * > .l-layout {
|
|
||||||
background: $editUIGridColorBg;
|
|
||||||
box-shadow: inset $editUIGridColorFg 0 0 2px 1px;
|
|
||||||
|
|
||||||
> [class*='grid-holder'] {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Styles moved to _global.scss;
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import uuid from 'uuid';
|
import LayoutItem from './LayoutItem.vue';
|
||||||
|
import TelemetryViewConfiguration from './../TelemetryViewConfiguration.js'
|
||||||
|
import SubobjectViewConfiguration from './../SubobjectViewConfiguration.js'
|
||||||
|
import ElementViewConfiguration from './../ElementViewConfiguration.js'
|
||||||
|
|
||||||
import SubobjectView from './SubobjectView.vue'
|
const DEFAULT_GRID_SIZE = [10, 10];
|
||||||
import TelemetryView from './TelemetryView.vue'
|
|
||||||
import BoxView from './BoxView.vue'
|
|
||||||
import TextView from './TextView.vue'
|
|
||||||
import LineView from './LineView.vue'
|
|
||||||
import ImageView from './ImageView.vue'
|
|
||||||
|
|
||||||
const ITEM_TYPE_VIEW_MAP = {
|
|
||||||
'subobject-view': SubobjectView,
|
|
||||||
'telemetry-view': TelemetryView,
|
|
||||||
'box-view': BoxView,
|
|
||||||
'line-view': LineView,
|
|
||||||
'text-view': TextView,
|
|
||||||
'image-view': ImageView
|
|
||||||
};
|
|
||||||
const ORDERS = {
|
|
||||||
top: Number.POSITIVE_INFINITY,
|
|
||||||
up: 1,
|
|
||||||
down: -1,
|
|
||||||
bottom: Number.NEGATIVE_INFINITY
|
|
||||||
};
|
|
||||||
|
|
||||||
function getItemDefinition(itemType, ...options) {
|
|
||||||
let itemView = ITEM_TYPE_VIEW_MAP[itemType];
|
|
||||||
|
|
||||||
if (!itemView) {
|
|
||||||
throw `Invalid itemType: ${itemType}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return itemView.makeDefinition(...options);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
let domainObject = JSON.parse(JSON.stringify(this.domainObject));
|
|
||||||
return {
|
return {
|
||||||
internalDomainObject: domainObject,
|
gridSize: [],
|
||||||
initSelectIndex: undefined
|
layoutItems: [],
|
||||||
};
|
drilledIn: undefined
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
gridSize() {
|
|
||||||
return this.internalDomainObject.configuration.layoutGrid;
|
|
||||||
},
|
|
||||||
layoutItems() {
|
|
||||||
return this.internalDomainObject.configuration.items;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
inject: ['openmct'],
|
inject: ['openmct'],
|
||||||
props: ['domainObject'],
|
props: ['domainObject'],
|
||||||
components: ITEM_TYPE_VIEW_MAP,
|
components: {
|
||||||
|
LayoutItem
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
addElement(itemType, element) {
|
getConfigurationItems() {
|
||||||
this.addItem(itemType + '-view', element);
|
this.subobjectViewsById = new Map();
|
||||||
|
this.telemetryViewsById = new Map();
|
||||||
|
let items = this.newDomainObject.configuration.items;
|
||||||
|
items.forEach((item, index) => {
|
||||||
|
if (item.type === 'subobject') {
|
||||||
|
this.subobjectViewsById.set(this.openmct.objects.makeKeyString(item.identifier), true);
|
||||||
|
} else if (item.type === 'telemetry') {
|
||||||
|
this.telemetryViewsById.set(this.openmct.objects.makeKeyString(item.identifier), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
item.index = index;
|
||||||
|
this.makeLayoutItem(item, false);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
makeLayoutItem(item, initSelect) {
|
||||||
|
if (item.type === 'telemetry') {
|
||||||
|
this.makeTelemetryItem(item, initSelect);
|
||||||
|
} else if (item.type === 'subobject') {
|
||||||
|
this.makeSubobjectItem(item, initSelect);
|
||||||
|
} else {
|
||||||
|
this.makeElementItem(item, initSelect);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
makeTelemetryItem(item, initSelect) {
|
||||||
|
let id = this.openmct.objects.makeKeyString(item.identifier);
|
||||||
|
this.openmct.objects.get(id).then(domainObject => {
|
||||||
|
let config = new TelemetryViewConfiguration({
|
||||||
|
domainObject: this.newDomainObject,
|
||||||
|
alphanumeric: item,
|
||||||
|
openmct: openmct,
|
||||||
|
gridSize: this.gridSize
|
||||||
|
});
|
||||||
|
this.layoutItems.push({
|
||||||
|
id: id,
|
||||||
|
domainObject: domainObject,
|
||||||
|
initSelect: initSelect,
|
||||||
|
type: item.type + '-view',
|
||||||
|
config: config
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
makeSubobjectItem(item, initSelect) {
|
||||||
|
let id = this.openmct.objects.makeKeyString(item.identifier);
|
||||||
|
this.openmct.objects.get(id).then(domainObject => {
|
||||||
|
let config = new SubobjectViewConfiguration({
|
||||||
|
domainObject: this.newDomainObject,
|
||||||
|
panel: item,
|
||||||
|
openmct: openmct,
|
||||||
|
gridSize: this.gridSize
|
||||||
|
});
|
||||||
|
this.layoutItems.push({
|
||||||
|
id: id,
|
||||||
|
domainObject: domainObject,
|
||||||
|
initSelect: initSelect,
|
||||||
|
type: item.type + '-view',
|
||||||
|
config: config,
|
||||||
|
drilledIn: this.isItemDrilledIn(id)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
makeElementItem(item, initSelect) {
|
||||||
|
let config = new ElementViewConfiguration({
|
||||||
|
domainObject: this.newDomainObject,
|
||||||
|
element: item,
|
||||||
|
openmct: openmct,
|
||||||
|
gridSize: this.gridSize
|
||||||
|
});
|
||||||
|
this.layoutItems.push({
|
||||||
|
initSelect: initSelect,
|
||||||
|
type: item.type + '-view',
|
||||||
|
config: config
|
||||||
|
});
|
||||||
},
|
},
|
||||||
setSelection(selection) {
|
setSelection(selection) {
|
||||||
if (selection.length === 0) {
|
if (selection.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.removeSelectionListener) {
|
this.updateDrilledInState();
|
||||||
this.removeSelectionListener();
|
|
||||||
}
|
|
||||||
|
|
||||||
let itemIndex = selection[0].context.index;
|
|
||||||
|
|
||||||
if (itemIndex !== undefined) {
|
|
||||||
this.attachSelectionListener(itemIndex);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
attachSelectionListener(index) {
|
updateDrilledInState(id) {
|
||||||
let path = `configuration.items[${index}].useGrid`;
|
this.drilledIn = id;
|
||||||
this.removeSelectionListener = this.openmct.objects.observe(this.internalDomainObject, path, function (value) {
|
this.layoutItems.forEach(function (item) {
|
||||||
let item = this.layoutItems[index];
|
if (item.type === 'subobject-view') {
|
||||||
|
item.drilledIn = item.id === id;
|
||||||
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));
|
isItemDrilledIn(id) {
|
||||||
|
return this.drilledIn === id;
|
||||||
|
},
|
||||||
|
updatePosition(item, newPosition) {
|
||||||
|
item.config.newPosition = newPosition;
|
||||||
|
item.config.updateStyle(newPosition);
|
||||||
},
|
},
|
||||||
bypassSelection($event) {
|
bypassSelection($event) {
|
||||||
if (this.dragInProgress) {
|
if (this.dragInProgress) {
|
||||||
@@ -214,56 +210,69 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
endDrag(item, updates) {
|
endDrag(item) {
|
||||||
this.dragInProgress = true;
|
this.dragInProgress = true;
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
this.dragInProgress = false;
|
this.dragInProgress = false;
|
||||||
}.bind(this), 0);
|
}.bind(this), 0);
|
||||||
|
// TODO: emit "finishResizing" for view components to mutate position?
|
||||||
let index = this.layoutItems.indexOf(item);
|
item.config.mutatePosition();
|
||||||
Object.assign(item, updates);
|
|
||||||
this.mutate(`configuration.items[${index}]`, item);
|
|
||||||
},
|
},
|
||||||
mutate(path, value) {
|
mutate(path, value) {
|
||||||
this.openmct.objects.mutate(this.internalDomainObject, path, value);
|
this.openmct.objects.mutate(this.newDomainObject, path, value);
|
||||||
},
|
},
|
||||||
handleDrop($event) {
|
handleDrop($event) {
|
||||||
if (!$event.dataTransfer.types.includes('openmct/domain-object-path')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$event.preventDefault();
|
$event.preventDefault();
|
||||||
|
|
||||||
let domainObject = JSON.parse($event.dataTransfer.getData('openmct/domain-object-path'))[0];
|
let domainObject = JSON.parse($event.dataTransfer.getData('domainObject'));
|
||||||
let elementRect = this.$el.getBoundingClientRect();
|
let elementRect = this.$el.getBoundingClientRect();
|
||||||
let droppedObjectPosition = [
|
this.droppedObjectPosition = [
|
||||||
Math.floor(($event.pageX - elementRect.left) / this.gridSize[0]),
|
Math.floor(($event.pageX - elementRect.left) / this.gridSize[0]),
|
||||||
Math.floor(($event.pageY - elementRect.top) / this.gridSize[1])
|
Math.floor(($event.pageY - elementRect.top) / this.gridSize[1])
|
||||||
];
|
];
|
||||||
|
|
||||||
if (this.isTelemetry(domainObject)) {
|
if (this.isTelemetry(domainObject)) {
|
||||||
this.addItem('telemetry-view', domainObject, droppedObjectPosition);
|
this.addTelemetryObject(domainObject, this.droppedObjectPosition);
|
||||||
} else {
|
} else {
|
||||||
let identifier = this.openmct.objects.makeKeyString(domainObject.identifier);
|
this.checkForDuplicatePanel(domainObject);
|
||||||
|
|
||||||
if (!this.objectViewMap[identifier]) {
|
|
||||||
this.addItem('subobject-view', domainObject, droppedObjectPosition);
|
|
||||||
} else {
|
|
||||||
let prompt = this.openmct.overlays.dialog({
|
|
||||||
iconClass: 'alert',
|
|
||||||
message: "This item is already in layout and will not be added again.",
|
|
||||||
buttons: [
|
|
||||||
{
|
|
||||||
label: 'OK',
|
|
||||||
callback: function () {
|
|
||||||
prompt.dismiss();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
checkForDuplicatePanel(domainObject) {
|
||||||
|
let id = this.openmct.objects.makeKeyString(domainObject.identifier);
|
||||||
|
let panels = this.newDomainObject.configuration.panels;
|
||||||
|
|
||||||
|
if (panels && panels[id]) {
|
||||||
|
let prompt = this.openmct.overlays.dialog({
|
||||||
|
iconClass: 'alert',
|
||||||
|
message: "This item is already in layout and will not be added again.",
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
label: 'OK',
|
||||||
|
callback: function () {
|
||||||
|
prompt.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
addItem(item) {
|
||||||
|
let items = this.newDomainObject.configuration.items || [];
|
||||||
|
item.index = items.push(item) - 1;
|
||||||
|
this.mutate("configuration.items", items);
|
||||||
|
this.makeLayoutItem(item, true);
|
||||||
|
},
|
||||||
|
addTelemetryObject(domainObject, position) {
|
||||||
|
let item = TelemetryViewConfiguration.create(domainObject, position, this.openmct);
|
||||||
|
this.addItem(item);
|
||||||
|
this.telemetryViewsById.set(this.openmct.objects.makeKeyString(domainObject.identifier), true);
|
||||||
|
},
|
||||||
|
addElement(type) {
|
||||||
|
Promise.resolve(ElementViewConfiguration.create(type, this.openmct))
|
||||||
|
.then(item => {
|
||||||
|
this.addItem(item);
|
||||||
|
});
|
||||||
|
},
|
||||||
handleDragOver($event){
|
handleDragOver($event){
|
||||||
$event.preventDefault();
|
$event.preventDefault();
|
||||||
},
|
},
|
||||||
@@ -275,136 +284,51 @@
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
addItem(itemType, ...options) {
|
addSubobject(domainObject) {
|
||||||
let item = getItemDefinition(itemType, this.openmct, this.gridSize, ...options);
|
let id = this.openmct.objects.makeKeyString(domainObject.identifier);
|
||||||
item.type = itemType;
|
|
||||||
item.id = uuid();
|
|
||||||
this.trackItem(item);
|
|
||||||
this.layoutItems.push(item);
|
|
||||||
this.openmct.objects.mutate(this.internalDomainObject, "configuration.items", this.layoutItems);
|
|
||||||
this.initSelectIndex = this.layoutItems.length - 1;
|
|
||||||
},
|
|
||||||
trackItem(item) {
|
|
||||||
if (!item.identifier) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let keyString = this.openmct.objects.makeKeyString(item.identifier);
|
if (this.isTelemetry(domainObject)) {
|
||||||
|
if (!this.telemetryViewsById.has(id)) {
|
||||||
if (item.type === "telemetry-view") {
|
this.addTelemetryObject(domainObject);
|
||||||
let count = this.telemetryViewMap[keyString] || 0;
|
|
||||||
this.telemetryViewMap[keyString] = ++count;
|
|
||||||
} else if (item.type === "subobject-view") {
|
|
||||||
this.objectViewMap[keyString] = true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
removeItem(item, index) {
|
|
||||||
this.initSelectIndex = -1;
|
|
||||||
this.layoutItems.splice(index, 1);
|
|
||||||
this.mutate("configuration.items", this.layoutItems);
|
|
||||||
this.untrackItem(item);
|
|
||||||
this.$el.click();
|
|
||||||
},
|
|
||||||
untrackItem(item) {
|
|
||||||
if (!item.identifier) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let keyString = this.openmct.objects.makeKeyString(item.identifier);
|
|
||||||
|
|
||||||
if (item.type === 'telemetry-view') {
|
|
||||||
let count = --this.telemetryViewMap[keyString];
|
|
||||||
|
|
||||||
if (count === 0) {
|
|
||||||
delete this.telemetryViewMap[keyString];
|
|
||||||
this.removeFromComposition(keyString);
|
|
||||||
}
|
}
|
||||||
} else if (item.type === 'subobject-view') {
|
} else {
|
||||||
delete this.objectViewMap[keyString];
|
if (!this.subobjectViewsById.has(id)) {
|
||||||
this.removeFromComposition(keyString);
|
let item = SubobjectViewConfiguration.create(domainObject, this.gridSize, this.droppedObjectPosition);
|
||||||
}
|
this.subobjectViewsById.set(id, true);
|
||||||
},
|
this.addItem(item, true);
|
||||||
removeFromComposition(keyString) {
|
delete this.droppedObjectPosition;
|
||||||
let composition = _.get(this.internalDomainObject, 'composition');
|
|
||||||
composition = composition.filter(identifier => {
|
|
||||||
return this.openmct.objects.makeKeyString(identifier) !== keyString;
|
|
||||||
});
|
|
||||||
this.mutate("composition", composition);
|
|
||||||
},
|
|
||||||
initializeItems() {
|
|
||||||
this.telemetryViewMap = {};
|
|
||||||
this.objectViewMap = {};
|
|
||||||
this.layoutItems.forEach(this.trackItem);
|
|
||||||
},
|
|
||||||
addChild(child) {
|
|
||||||
let identifier = this.openmct.objects.makeKeyString(child.identifier);
|
|
||||||
if (this.isTelemetry(child)) {
|
|
||||||
if (!this.telemetryViewMap[identifier]) {
|
|
||||||
this.addItem('telemetry-view', child);
|
|
||||||
}
|
|
||||||
} else if (!this.objectViewMap[identifier]) {
|
|
||||||
this.addItem('subobject-view', child);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
removeChild(identifier) {
|
|
||||||
let keyString = this.openmct.objects.makeKeyString(identifier);
|
|
||||||
|
|
||||||
if (this.objectViewMap[keyString]) {
|
|
||||||
delete this.objectViewMap[keyString];
|
|
||||||
this.removeFromConfiguration(keyString);
|
|
||||||
} else if (this.telemetryViewMap[keyString]) {
|
|
||||||
delete this.telemetryViewMap[keyString];
|
|
||||||
this.removeFromConfiguration(keyString);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
removeFromConfiguration(keyString) {
|
|
||||||
let layoutItems = this.layoutItems.filter(item => {
|
|
||||||
if (!item.identifier) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return this.openmct.objects.makeKeyString(item.identifier) !== keyString;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.mutate("configuration.items", layoutItems);
|
|
||||||
this.$el.click();
|
|
||||||
},
|
|
||||||
orderItem(position, index) {
|
|
||||||
let delta = ORDERS[position];
|
|
||||||
let newIndex = Math.max(Math.min(index + delta, this.layoutItems.length - 1), 0);
|
|
||||||
let item = this.layoutItems[index];
|
|
||||||
|
|
||||||
if (newIndex !== index) {
|
|
||||||
this.layoutItems.splice(index, 1);
|
|
||||||
this.layoutItems.splice(newIndex, 0, item);
|
|
||||||
this.mutate('configuration.items', this.layoutItems);
|
|
||||||
|
|
||||||
if (this.removeSelectionListener) {
|
|
||||||
this.removeSelectionListener();
|
|
||||||
this.attachSelectionListener(newIndex);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
removeSubobject() {
|
||||||
|
// Not yet implemented
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.unlisten = this.openmct.objects.observe(this.internalDomainObject, '*', function (obj) {
|
this.newDomainObject = this.domainObject;
|
||||||
this.internalDomainObject = JSON.parse(JSON.stringify(obj));
|
this.gridSize = this.newDomainObject.layoutGrid || DEFAULT_GRID_SIZE;
|
||||||
|
this.unlisten = this.openmct.objects.observe(this.newDomainObject, '*', function (obj) {
|
||||||
|
this.newDomainObject = JSON.parse(JSON.stringify(obj));
|
||||||
|
this.gridSize = this.newDomainObject.layoutGrid || DEFAULT_GRID_SIZE;;
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
|
||||||
this.openmct.selection.on('change', this.setSelection);
|
this.openmct.selection.on('change', this.setSelection);
|
||||||
this.initializeItems();
|
this.getConfigurationItems();
|
||||||
this.composition = this.openmct.composition.get(this.internalDomainObject);
|
|
||||||
this.composition.on('add', this.addChild);
|
this.composition = this.openmct.composition.get(this.newDomainObject);
|
||||||
this.composition.on('remove', this.removeChild);
|
this.composition.on('add', this.addSubobject);
|
||||||
|
this.composition.on('remove', this.removeSubobject);
|
||||||
this.composition.load();
|
this.composition.load();
|
||||||
},
|
},
|
||||||
destroyed: function () {
|
destroyed: function () {
|
||||||
this.openmct.off('change', this.setSelection);
|
this.openmct.off('change', this.setSelection);
|
||||||
this.composition.off('add', this.addChild);
|
this.composition.off('add', this.addSubobject);
|
||||||
this.composition.off('remove', this.removeChild);
|
this.composition.off('remove', this.removeSubobject);
|
||||||
this.unlisten();
|
this.unlisten();
|
||||||
|
|
||||||
if (this.removeSelectionListener) {
|
delete this.subobjectViewsById;
|
||||||
this.removeSelectionListener();
|
delete this.telemetryViewsById;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
|
||||||
|
</script>
|
||||||
@@ -21,13 +21,9 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<layout-frame :item="item"
|
<div class="c-image-view"
|
||||||
:grid-size="gridSize"
|
:style="styleObject">
|
||||||
@endDrag="(item, updates) => $emit('endDrag', item, updates)">
|
</div>
|
||||||
<div class="c-image-view"
|
|
||||||
:style="style">
|
|
||||||
</div>
|
|
||||||
</layout-frame>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@@ -46,59 +42,24 @@
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import LayoutFrame from './LayoutFrame.vue'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
makeDefinition(openmct, gridSize, element) {
|
|
||||||
return {
|
|
||||||
stroke: 'transparent',
|
|
||||||
x: 1,
|
|
||||||
y: 1,
|
|
||||||
width: 10,
|
|
||||||
height: 5,
|
|
||||||
url: element.url,
|
|
||||||
useGrid: true
|
|
||||||
};
|
|
||||||
},
|
|
||||||
inject: ['openmct'],
|
|
||||||
props: {
|
props: {
|
||||||
item: Object,
|
item: Object
|
||||||
gridSize: Array,
|
|
||||||
index: Number,
|
|
||||||
initSelect: Boolean
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
LayoutFrame
|
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
style() {
|
styleObject() {
|
||||||
|
let element = this.item.config.element;
|
||||||
return {
|
return {
|
||||||
backgroundImage: 'url(' + this.item.url + ')',
|
backgroundImage: 'url(' + element.url + ')',
|
||||||
border: '1px solid ' + this.item.stroke
|
border: '1px solid ' + element.stroke
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
|
||||||
index(newIndex) {
|
|
||||||
if (!this.context) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.context.index = newIndex;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.context = {
|
this.item.config.attachListeners();
|
||||||
layoutItem: this.item,
|
|
||||||
index: this.index
|
|
||||||
};
|
|
||||||
this.removeSelectable = this.openmct.selection.selectable(
|
|
||||||
this.$el, this.context, this.initSelect);
|
|
||||||
},
|
},
|
||||||
destroyed() {
|
destroyed() {
|
||||||
if (this.removeSelectable) {
|
this.item.config.removeListeners();
|
||||||
this.removeSelectable();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -1,300 +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>
|
|
||||||
<div class="l-layout__frame c-frame"
|
|
||||||
:class="{
|
|
||||||
'no-frame': !item.hasFrame,
|
|
||||||
'u-inspectable': inspectable,
|
|
||||||
'is-resizing': isResizing
|
|
||||||
}"
|
|
||||||
:style="style">
|
|
||||||
|
|
||||||
<slot></slot>
|
|
||||||
|
|
||||||
<!-- 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>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
@import "~styles/sass-base";
|
|
||||||
|
|
||||||
/******************************* FRAME */
|
|
||||||
.c-frame {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
// Whatever is placed into the slot, make it fill the entirety of the space, obeying padding
|
|
||||||
> *:first-child {
|
|
||||||
flex: 1 1 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:not(.no-frame) {
|
|
||||||
background: $colorBodyBg;
|
|
||||||
border: $browseFrameBorder;
|
|
||||||
padding: $interiorMargin;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.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 {
|
|
||||||
.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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&[s-selected] {
|
|
||||||
// All frames selected while editing
|
|
||||||
border: $editFrameSelectedBorder;
|
|
||||||
box-shadow: $editFrameSelectedShdw;
|
|
||||||
|
|
||||||
> .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'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
inject: ['openmct'],
|
|
||||||
props: {
|
|
||||||
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',
|
|
||||||
width: (this.gridSize[0] * width) + 'px',
|
|
||||||
height: (this.gridSize[1] * height) + 'px',
|
|
||||||
minWidth: (this.gridSize[0] * width) + 'px',
|
|
||||||
minHeight: (this.gridSize[1] * height) + 'px'
|
|
||||||
};
|
|
||||||
},
|
|
||||||
inspectable() {
|
|
||||||
return this.item.type === 'subobject-view' || this.item.type === 'telemetry-view';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
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));
|
|
||||||
},
|
|
||||||
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],
|
|
||||||
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();
|
|
||||||
},
|
|
||||||
continueDrag(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
this.updatePosition(event);
|
|
||||||
this.dragPosition = this.activeDrag.getAdjustedPosition(this.delta);
|
|
||||||
},
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
188
src/plugins/displayLayout/components/LayoutItem.vue
Normal file
188
src/plugins/displayLayout/components/LayoutItem.vue
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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>
|
||||||
|
<div class="c-frame has-local-controls"
|
||||||
|
:style="item.config.style"
|
||||||
|
:class="[classObject, {
|
||||||
|
'u-inspectable': item.config.inspectable()
|
||||||
|
}]"
|
||||||
|
@dblclick="drill(item.id, $event)">
|
||||||
|
|
||||||
|
<component :is="item.type" :item="item" :gridSize="gridSize"></component>
|
||||||
|
|
||||||
|
<!-- Drag handles -->
|
||||||
|
<div class="c-frame-edit">
|
||||||
|
<div class="c-frame-edit__move"
|
||||||
|
@mousedown="startDrag([1,1], [0,0], $event)"></div>
|
||||||
|
<div class="c-frame-edit__handle c-frame-edit__handle--nw"
|
||||||
|
@mousedown="startDrag([1,1], [-1,-1], $event)"></div>
|
||||||
|
<div class="c-frame-edit__handle c-frame-edit__handle--ne"
|
||||||
|
@mousedown="startDrag([0,1], [1,-1], $event)"></div>
|
||||||
|
<div class="c-frame-edit__handle c-frame-edit__handle--sw"
|
||||||
|
@mousedown="startDrag([1,0], [-1,1], $event)"></div>
|
||||||
|
<div class="c-frame-edit__handle c-frame-edit__handle--se"
|
||||||
|
@mousedown="startDrag([0,0], [1,1], $event)"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import "~styles/sass-base";
|
||||||
|
|
||||||
|
/******************************* FRAME */
|
||||||
|
.c-frame {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
|
||||||
|
/*************************** NO-FRAME */
|
||||||
|
&.no-frame {
|
||||||
|
> [class*="contents"] > [class*="__header"] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(.no-frame) {
|
||||||
|
background: $colorBodyBg;
|
||||||
|
border: 1px solid $colorInteriorBorder;
|
||||||
|
padding: $interiorMargin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import SubobjectView from './SubobjectView.vue'
|
||||||
|
import TelemetryView from './TelemetryView.vue'
|
||||||
|
import BoxView from './BoxView.vue'
|
||||||
|
import TextView from './TextView.vue'
|
||||||
|
import LineView from './LineView.vue'
|
||||||
|
import ImageView from './ImageView.vue'
|
||||||
|
import LayoutDrag from './../LayoutDrag'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
inject: ['openmct'],
|
||||||
|
props: {
|
||||||
|
item: Object,
|
||||||
|
gridSize: Array
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
SubobjectView,
|
||||||
|
TelemetryView,
|
||||||
|
BoxView,
|
||||||
|
TextView,
|
||||||
|
LineView,
|
||||||
|
ImageView
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
classObject: function () {
|
||||||
|
return {
|
||||||
|
'is-drilled-in': this.item.drilledIn,
|
||||||
|
'no-frame': !this.item.config.hasFrame()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
drill(id, $event) {
|
||||||
|
if ($event) {
|
||||||
|
$event.stopPropagation();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.openmct.editor.isEditing()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.item.domainObject) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.openmct.composition.get(this.item.domainObject) === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable for fixed position.
|
||||||
|
if (this.item.domainObject.type === 'telemetry.fixed') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$emit('drilledIn', id);
|
||||||
|
},
|
||||||
|
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));
|
||||||
|
},
|
||||||
|
startDrag(posFactor, dimFactor, event) {
|
||||||
|
document.body.addEventListener('mousemove', this.continueDrag);
|
||||||
|
document.body.addEventListener('mouseup', this.endDrag);
|
||||||
|
|
||||||
|
this.updatePosition(event);
|
||||||
|
this.activeDrag = new LayoutDrag(
|
||||||
|
this.item.config.position(),
|
||||||
|
posFactor,
|
||||||
|
dimFactor,
|
||||||
|
this.gridSize
|
||||||
|
);
|
||||||
|
event.preventDefault();
|
||||||
|
},
|
||||||
|
continueDrag(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
this.updatePosition(event);
|
||||||
|
|
||||||
|
if (this.activeDrag) {
|
||||||
|
this.$emit(
|
||||||
|
'dragInProgress',
|
||||||
|
this.item,
|
||||||
|
this.activeDrag.getAdjustedPosition(this.delta)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
endDrag(event) {
|
||||||
|
document.body.removeEventListener('mousemove', this.continueDrag);
|
||||||
|
document.body.removeEventListener('mouseup', this.endDrag);
|
||||||
|
this.continueDrag(event);
|
||||||
|
this.$emit('endDrag', this.item);
|
||||||
|
this.initialPosition = undefined;
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
let context = {
|
||||||
|
item: this.item.domainObject,
|
||||||
|
layoutItem: this.item,
|
||||||
|
addElement: this.$parent.addElement
|
||||||
|
};
|
||||||
|
|
||||||
|
this.removeSelectable = this.openmct.selection.selectable(
|
||||||
|
this.$el,
|
||||||
|
context,
|
||||||
|
this.item.initSelect
|
||||||
|
);
|
||||||
|
},
|
||||||
|
destroyed() {
|
||||||
|
this.removeSelectable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -20,214 +20,37 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="l-layout__frame c-frame no-frame"
|
<div>
|
||||||
:style="style">
|
<svg :width="gridSize[0] * element.width"
|
||||||
<svg width="100%" height="100%">
|
:height=" gridSize[1] * element.height">
|
||||||
<line v-bind="linePosition"
|
<line :x1=" gridSize[0] * element.x1 + 1"
|
||||||
:stroke="item.stroke"
|
:y1="gridSize[1] * element.y1 + 1 "
|
||||||
stroke-width="2">
|
:x2="gridSize[0] * element.x2 + 1"
|
||||||
</line>
|
:y2=" gridSize[1] * element.y2 + 1 "
|
||||||
</svg>
|
:stroke="element.stroke"
|
||||||
|
stroke-width="2">
|
||||||
<div class="c-frame-edit">
|
</line>
|
||||||
<div class="c-frame-edit__move"
|
</svg>
|
||||||
@mousedown="startDrag($event)"></div>
|
</div>
|
||||||
<div class="c-frame-edit__handle"
|
</template>
|
||||||
:class="startHandleClass"
|
|
||||||
@mousedown="startDrag($event, 'start')"></div>
|
|
||||||
<div class="c-frame-edit__handle"
|
|
||||||
:class="endHandleClass"
|
|
||||||
@mousedown="startDrag($event, 'end')"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
const START_HANDLE_QUADRANTS = {
|
|
||||||
1: 'c-frame-edit__handle--sw',
|
|
||||||
2: 'c-frame-edit__handle--se',
|
|
||||||
3: 'c-frame-edit__handle--ne',
|
|
||||||
4: 'c-frame-edit__handle--nw'
|
|
||||||
};
|
|
||||||
|
|
||||||
const END_HANDLE_QUADRANTS = {
|
|
||||||
1: 'c-frame-edit__handle--ne',
|
|
||||||
2: 'c-frame-edit__handle--nw',
|
|
||||||
3: 'c-frame-edit__handle--sw',
|
|
||||||
4: 'c-frame-edit__handle--se'
|
|
||||||
};
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
makeDefinition() {
|
|
||||||
return {
|
|
||||||
x: 5,
|
|
||||||
y: 10,
|
|
||||||
x2: 10,
|
|
||||||
y2: 5,
|
|
||||||
stroke: '#717171',
|
|
||||||
useGrid: true
|
|
||||||
};
|
|
||||||
},
|
|
||||||
inject: ['openmct'],
|
|
||||||
props: {
|
props: {
|
||||||
item: Object,
|
item: Object,
|
||||||
gridSize: Array,
|
gridSize: Array
|
||||||
initSelect: Boolean,
|
|
||||||
index: Number,
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
dragPosition: undefined
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
position() {
|
element() {
|
||||||
let {x, y, x2, y2} = this.item;
|
return this.item.config.element;
|
||||||
if (this.dragPosition) {
|
|
||||||
({x, y, x2, y2} = this.dragPosition);
|
|
||||||
}
|
|
||||||
return {x, y, x2, y2};
|
|
||||||
},
|
|
||||||
style() {
|
|
||||||
let {x, y, x2, y2} = this.position;
|
|
||||||
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 {
|
|
||||||
left: `${left}px`,
|
|
||||||
top: `${top}px`,
|
|
||||||
width: `${width}px`,
|
|
||||||
height: `${height}px`,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
startHandleClass() {
|
|
||||||
return START_HANDLE_QUADRANTS[this.vectorQuadrant];
|
|
||||||
},
|
|
||||||
endHandleClass() {
|
|
||||||
return END_HANDLE_QUADRANTS[this.vectorQuadrant];
|
|
||||||
},
|
|
||||||
vectorQuadrant() {
|
|
||||||
let {x, y, x2, y2} = this.position;
|
|
||||||
if (x2 > x) {
|
|
||||||
if (y2 < y) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 4;
|
|
||||||
}
|
|
||||||
if (y2 < y) {
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
return 3;
|
|
||||||
},
|
|
||||||
linePosition() {
|
|
||||||
if (this.vectorQuadrant === 1) {
|
|
||||||
return {
|
|
||||||
x1: '0%',
|
|
||||||
y1: '100%',
|
|
||||||
x2: '100%',
|
|
||||||
y2: '0%'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (this.vectorQuadrant === 4) {
|
|
||||||
return {
|
|
||||||
x1: '0%',
|
|
||||||
y1: '0%',
|
|
||||||
x2: '100%',
|
|
||||||
y2: '100%'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (this.vectorQuadrant === 2) {
|
|
||||||
return {
|
|
||||||
x1: '0%',
|
|
||||||
y1: '0%',
|
|
||||||
x2: '100%',
|
|
||||||
y2: '100%'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (this.vectorQuadrant === 3) {
|
|
||||||
return {
|
|
||||||
x1: '100%',
|
|
||||||
y1: '0%',
|
|
||||||
x2: '0%',
|
|
||||||
y2: '100%'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
startDrag(event, position) {
|
|
||||||
this.dragging = position;
|
|
||||||
document.body.addEventListener('mousemove', this.continueDrag);
|
|
||||||
document.body.addEventListener('mouseup', this.endDrag);
|
|
||||||
this.startPosition = [event.pageX, event.pageY];
|
|
||||||
this.dragPosition = {
|
|
||||||
x: this.item.x,
|
|
||||||
y: this.item.y,
|
|
||||||
x2: this.item.x2,
|
|
||||||
y2: this.item.y2
|
|
||||||
};
|
|
||||||
event.preventDefault();
|
|
||||||
},
|
|
||||||
continueDrag(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
let pxDeltaX = this.startPosition[0] - event.pageX;
|
|
||||||
let pxDeltaY = this.startPosition[1] - event.pageY;
|
|
||||||
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;
|
|
||||||
this.$emit('endDrag', this.item, {x, y, x2, y2});
|
|
||||||
this.dragPosition = undefined;
|
|
||||||
this.dragging = undefined;
|
|
||||||
event.preventDefault();
|
|
||||||
},
|
|
||||||
calculateDragPosition(pxDeltaX, pxDeltaY) {
|
|
||||||
let gridDeltaX = Math.round(pxDeltaX / this.gridSize[0]);
|
|
||||||
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;
|
|
||||||
} else if (this.dragging === 'end') {
|
|
||||||
dragPosition.x2 -= gridDeltaX;
|
|
||||||
dragPosition.y2 -= gridDeltaY;
|
|
||||||
} else {
|
|
||||||
// dragging entire line.
|
|
||||||
dragPosition.x -= gridDeltaX;
|
|
||||||
dragPosition.y -= gridDeltaY;
|
|
||||||
dragPosition.x2 -= gridDeltaX;
|
|
||||||
dragPosition.y2 -= gridDeltaY;
|
|
||||||
}
|
|
||||||
return dragPosition;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
index(newIndex) {
|
|
||||||
if (!this.context) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.context.index = newIndex;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.context = {
|
this.item.config.attachListeners();
|
||||||
layoutItem: this.item,
|
|
||||||
index: this.index
|
|
||||||
};
|
|
||||||
this.removeSelectable = this.openmct.selection.selectable(
|
|
||||||
this.$el, this.context, this.initSelect);
|
|
||||||
},
|
},
|
||||||
destroyed() {
|
destroyed() {
|
||||||
if (this.removeSelectable) {
|
this.item.config.removeListeners();
|
||||||
this.removeSelectable();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -20,101 +20,99 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
<template>
|
<template>
|
||||||
<layout-frame :item="item"
|
<div class="u-contents">
|
||||||
:grid-size="gridSize"
|
<div class="c-so-view__header">
|
||||||
@endDrag="(item, updates) => $emit('endDrag', item, updates)">
|
<div class="c-so-view__header__start">
|
||||||
<object-frame v-if="domainObject"
|
<div class="c-so-view__name icon-object">{{ item.domainObject.name }}</div>
|
||||||
:domain-object="domainObject"
|
<div class="c-so-view__context-actions c-disclosure-button"></div>
|
||||||
:object-path="objectPath"
|
</div>
|
||||||
:has-frame="item.hasFrame">
|
<div class="c-so-view__header__end">
|
||||||
</object-frame>
|
<div class="c-button icon-expand local-controls--hidden"></div>
|
||||||
</layout-frame>
|
</div>
|
||||||
|
</div>
|
||||||
|
<object-view class="c-so-view__object-view"
|
||||||
|
:object="item.domainObject"></object-view>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<style lang="scss">
|
||||||
import ObjectFrame from '../../../ui/components/ObjectFrame.vue'
|
@import "~styles/sass-base";
|
||||||
import LayoutFrame from './LayoutFrame.vue'
|
|
||||||
|
|
||||||
const MINIMUM_FRAME_SIZE = [320, 180],
|
.c-so-view {
|
||||||
DEFAULT_DIMENSIONS = [10, 10],
|
/*************************** HEADER */
|
||||||
DEFAULT_POSITION = [1, 1],
|
&__header {
|
||||||
DEFAULT_HIDDEN_FRAME_TYPES = ['hyperlink', 'summary-widget'];
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
margin-bottom: $interiorMargin;
|
||||||
|
|
||||||
function getDefaultDimensions(gridSize) {
|
> [class*="__"] {
|
||||||
return MINIMUM_FRAME_SIZE.map((min, index) => {
|
display: flex;
|
||||||
return Math.max(
|
align-items: center;
|
||||||
Math.ceil(min / gridSize[index]),
|
|
||||||
DEFAULT_DIMENSIONS[index]
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function hasFrameByDefault(type) {
|
|
||||||
return DEFAULT_HIDDEN_FRAME_TYPES.indexOf(type) === -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
|
||||||
makeDefinition(openmct, gridSize, domainObject, position) {
|
|
||||||
let defaultDimensions = getDefaultDimensions(gridSize);
|
|
||||||
position = position || DEFAULT_POSITION;
|
|
||||||
|
|
||||||
return {
|
|
||||||
width: defaultDimensions[0],
|
|
||||||
height: defaultDimensions[1],
|
|
||||||
x: position[0],
|
|
||||||
y: position[1],
|
|
||||||
identifier: domainObject.identifier,
|
|
||||||
hasFrame: hasFrameByDefault(domainObject.type),
|
|
||||||
useGrid: true
|
|
||||||
};
|
|
||||||
},
|
|
||||||
inject: ['openmct'],
|
|
||||||
props: {
|
|
||||||
item: Object,
|
|
||||||
gridSize: Array,
|
|
||||||
initSelect: Boolean,
|
|
||||||
index: Number
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
domainObject: undefined,
|
|
||||||
objectPath: []
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
components: {
|
> * + * {
|
||||||
ObjectFrame,
|
margin-left: $interiorMargin;
|
||||||
LayoutFrame
|
}
|
||||||
},
|
|
||||||
watch: {
|
[class*="__start"] {
|
||||||
index(newIndex) {
|
flex: 1 1 auto;
|
||||||
if (!this.context) {
|
overflow: hidden;
|
||||||
return;
|
}
|
||||||
|
|
||||||
|
[class*="__end"] {
|
||||||
|
//justify-content: flex-end;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
|
||||||
|
[class*="button"] {
|
||||||
|
font-size: 0.7em;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.context.index = newIndex;
|
&__name {
|
||||||
|
@include ellipsize();
|
||||||
|
flex: 0 1 auto;
|
||||||
|
font-size: 1.2em;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
// Object type icon
|
||||||
|
flex: 0 0 auto;
|
||||||
|
margin-right: $interiorMarginSm;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
methods: {
|
|
||||||
setObject(domainObject) {
|
/*************************** OBJECT VIEW */
|
||||||
this.domainObject = domainObject;
|
&__object-view {
|
||||||
this.objectPath = [this.domainObject].concat(this.openmct.router.path);
|
flex: 1 1 auto;
|
||||||
this.context = {
|
overflow: auto;
|
||||||
item: domainObject,
|
|
||||||
layoutItem: this.item,
|
.c-object-view {
|
||||||
index: this.index
|
.u-fills-container {
|
||||||
};
|
// Expand component types that fill a container
|
||||||
this.removeSelectable = this.openmct.selection.selectable(
|
@include abs();
|
||||||
this.$el, this.context, this.initSelect);
|
}
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.openmct.objects.get(this.item.identifier)
|
|
||||||
.then(this.setObject);
|
|
||||||
},
|
|
||||||
destroyed() {
|
|
||||||
if (this.removeSelectable) {
|
|
||||||
this.removeSelectable();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ObjectView from '../../../ui/components/layout/ObjectView.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
inject: ['openmct'],
|
||||||
|
props: {
|
||||||
|
item: Object
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
ObjectView,
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.item.config.attachListeners();
|
||||||
|
},
|
||||||
|
destroyed() {
|
||||||
|
this.item.config.removeListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -21,25 +21,20 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<layout-frame :item="item"
|
<div class="c-telemetry-view"
|
||||||
:grid-size="gridSize"
|
:style="styleObject">
|
||||||
@endDrag="(item, updates) => $emit('endDrag', item, updates)">
|
<div v-if="showLabel"
|
||||||
<div class="c-telemetry-view"
|
class="c-telemetry-view__label">
|
||||||
:style="styleObject"
|
<div class="c-telemetry-view__label-text">{{ item.domainObject.name }}</div>
|
||||||
v-if="domainObject">
|
|
||||||
<div v-if="showLabel"
|
|
||||||
class="c-telemetry-view__label">
|
|
||||||
<div class="c-telemetry-view__label-text">{{ domainObject.name }}</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="showValue"
|
|
||||||
:title="fieldName"
|
|
||||||
class="c-telemetry-view__value"
|
|
||||||
:class="[telemetryClass]">
|
|
||||||
<div class="c-telemetry-view__value-text">{{ telemetryValue }}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</layout-frame>
|
|
||||||
|
<div v-if="showValue"
|
||||||
|
:title="fieldName"
|
||||||
|
class="c-telemetry-view__value"
|
||||||
|
:class="[telemetryClass]">
|
||||||
|
<div class="c-telemetry-view__value-text">{{ telemetryValue }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@@ -77,66 +72,37 @@
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import LayoutFrame from './LayoutFrame.vue'
|
|
||||||
|
|
||||||
const DEFAULT_TELEMETRY_DIMENSIONS = [10, 5],
|
|
||||||
DEFAULT_POSITION = [1, 1];
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
makeDefinition(openmct, gridSize, domainObject, position) {
|
|
||||||
let metadata = openmct.telemetry.getMetadata(domainObject);
|
|
||||||
position = position || DEFAULT_POSITION;
|
|
||||||
|
|
||||||
return {
|
|
||||||
identifier: domainObject.identifier,
|
|
||||||
x: position[0],
|
|
||||||
y: position[1],
|
|
||||||
width: DEFAULT_TELEMETRY_DIMENSIONS[0],
|
|
||||||
height: DEFAULT_TELEMETRY_DIMENSIONS[1],
|
|
||||||
displayMode: 'all',
|
|
||||||
value: metadata.getDefaultDisplayValue(),
|
|
||||||
stroke: "transparent",
|
|
||||||
fill: "",
|
|
||||||
color: "",
|
|
||||||
size: "13px",
|
|
||||||
useGrid: true
|
|
||||||
};
|
|
||||||
},
|
|
||||||
inject: ['openmct'],
|
inject: ['openmct'],
|
||||||
props: {
|
props: {
|
||||||
item: Object,
|
item: Object
|
||||||
gridSize: Array,
|
|
||||||
initSelect: Boolean,
|
|
||||||
index: Number
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
LayoutFrame
|
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
showLabel() {
|
showLabel() {
|
||||||
let displayMode = this.item.displayMode;
|
let displayMode = this.item.config.alphanumeric.displayMode;
|
||||||
return displayMode === 'all' || displayMode === 'label';
|
return displayMode === 'all' || displayMode === 'label';
|
||||||
},
|
},
|
||||||
showValue() {
|
showValue() {
|
||||||
let displayMode = this.item.displayMode;
|
let displayMode = this.item.config.alphanumeric.displayMode;
|
||||||
return displayMode === 'all' || displayMode === 'value';
|
return displayMode === 'all' || displayMode === 'value';
|
||||||
},
|
},
|
||||||
styleObject() {
|
styleObject() {
|
||||||
|
let alphanumeric = this.item.config.alphanumeric;
|
||||||
return {
|
return {
|
||||||
backgroundColor: this.item.fill,
|
backgroundColor: alphanumeric.fill,
|
||||||
borderColor: this.item.stroke,
|
borderColor: alphanumeric.stroke,
|
||||||
color: this.item.color,
|
color: alphanumeric.color,
|
||||||
fontSize: this.item.size
|
fontSize: alphanumeric.size
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
fieldName() {
|
fieldName() {
|
||||||
return this.valueMetadata && this.valueMetadata.name;
|
return this.valueMetadata.name;
|
||||||
},
|
},
|
||||||
valueMetadata() {
|
valueMetadata() {
|
||||||
return this.datum && this.metadata.value(this.item.value);
|
return this.metadata.value(this.item.config.alphanumeric.value);
|
||||||
},
|
},
|
||||||
valueFormatter() {
|
valueFormatter() {
|
||||||
return this.formats[this.item.value];
|
return this.formats[this.item.config.alphanumeric.value];
|
||||||
},
|
},
|
||||||
telemetryValue() {
|
telemetryValue() {
|
||||||
if (!this.datum) {
|
if (!this.datum) {
|
||||||
@@ -156,18 +122,8 @@
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
datum: undefined,
|
datum: {},
|
||||||
formats: undefined,
|
formats: {}
|
||||||
domainObject: undefined
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
index(newIndex) {
|
|
||||||
if (!this.context) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.context.index = newIndex;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@@ -178,7 +134,7 @@
|
|||||||
end: bounds.end,
|
end: bounds.end,
|
||||||
size: 1
|
size: 1
|
||||||
};
|
};
|
||||||
this.openmct.telemetry.request(this.domainObject, options)
|
this.openmct.telemetry.request(this.item.domainObject, options)
|
||||||
.then(data => {
|
.then(data => {
|
||||||
if (data.length > 0) {
|
if (data.length > 0) {
|
||||||
this.updateView(data[data.length - 1]);
|
this.updateView(data[data.length - 1]);
|
||||||
@@ -186,7 +142,7 @@
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
subscribeToObject() {
|
subscribeToObject() {
|
||||||
this.subscription = this.openmct.telemetry.subscribe(this.domainObject, function (datum) {
|
this.subscription = this.openmct.telemetry.subscribe(this.item.domainObject, function (datum) {
|
||||||
if (this.openmct.time.clock() !== undefined) {
|
if (this.openmct.time.clock() !== undefined) {
|
||||||
this.updateView(datum);
|
this.updateView(datum);
|
||||||
}
|
}
|
||||||
@@ -204,40 +160,28 @@
|
|||||||
refreshData(bounds, isTick) {
|
refreshData(bounds, isTick) {
|
||||||
if (!isTick) {
|
if (!isTick) {
|
||||||
this.datum = undefined;
|
this.datum = undefined;
|
||||||
this.requestHistoricalData(this.domainObject);
|
this.requestHistoricalData(this.item.domainObject);
|
||||||
}
|
}
|
||||||
},
|
|
||||||
setObject(domainObject) {
|
|
||||||
this.domainObject = domainObject;
|
|
||||||
this.metadata = this.openmct.telemetry.getMetadata(this.domainObject);
|
|
||||||
this.limitEvaluator = this.openmct.telemetry.limitEvaluator(this.domainObject);
|
|
||||||
this.formats = this.openmct.telemetry.getFormatMap(this.metadata);
|
|
||||||
this.requestHistoricalData();
|
|
||||||
this.subscribeToObject();
|
|
||||||
|
|
||||||
this.context = {
|
|
||||||
item: domainObject,
|
|
||||||
layoutItem: this.item,
|
|
||||||
index: this.index
|
|
||||||
};
|
|
||||||
this.removeSelectable = this.openmct.selection.selectable(
|
|
||||||
this.$el, this.context, this.initSelect);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
created() {
|
||||||
|
this.metadata = this.openmct.telemetry.getMetadata(this.item.domainObject);
|
||||||
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.openmct.objects.get(this.item.identifier)
|
this.limitEvaluator = this.openmct.telemetry.limitEvaluator(this.item.domainObject);
|
||||||
.then(this.setObject);
|
this.formats = this.openmct.telemetry.getFormatMap(this.metadata);
|
||||||
|
|
||||||
|
this.requestHistoricalData();
|
||||||
|
this.subscribeToObject();
|
||||||
|
|
||||||
|
this.item.config.attachListeners();
|
||||||
this.openmct.time.on("bounds", this.refreshData);
|
this.openmct.time.on("bounds", this.refreshData);
|
||||||
},
|
},
|
||||||
destroyed() {
|
destroyed() {
|
||||||
this.removeSubscription();
|
this.removeSubscription();
|
||||||
|
this.item.config.removeListeners();
|
||||||
if (this.removeSelectable) {
|
|
||||||
this.removeSelectable();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.openmct.time.off("bounds", this.refreshData);
|
this.openmct.time.off("bounds", this.refreshData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
@@ -21,14 +21,10 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<layout-frame :item="item"
|
<div class="c-text-view"
|
||||||
:grid-size="gridSize"
|
:style="styleObject">
|
||||||
@endDrag="(item, updates) => $emit('endDrag', item, updates)">
|
{{ item.config.element.text }}
|
||||||
<div class="c-text-view"
|
</div>
|
||||||
:style="style">
|
|
||||||
{{ item.text }}
|
|
||||||
</div>
|
|
||||||
</layout-frame>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@@ -46,65 +42,26 @@
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import LayoutFrame from './LayoutFrame.vue'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
makeDefinition(openmct, gridSize, element) {
|
|
||||||
return {
|
|
||||||
fill: 'transparent',
|
|
||||||
stroke: 'transparent',
|
|
||||||
size: '13px',
|
|
||||||
color: '',
|
|
||||||
x: 1,
|
|
||||||
y: 1,
|
|
||||||
width: 10,
|
|
||||||
height: 5,
|
|
||||||
text: element.text,
|
|
||||||
useGrid: true
|
|
||||||
};
|
|
||||||
},
|
|
||||||
inject: ['openmct'],
|
|
||||||
props: {
|
props: {
|
||||||
item: Object,
|
item: Object
|
||||||
gridSize: Array,
|
|
||||||
index: Number,
|
|
||||||
initSelect: Boolean
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
LayoutFrame
|
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
style() {
|
styleObject() {
|
||||||
|
let element = this.item.config.element;
|
||||||
return {
|
return {
|
||||||
backgroundColor: this.item.fill,
|
backgroundColor: element.fill,
|
||||||
borderColor: this.item.stroke,
|
borderColor: element.stroke,
|
||||||
color: this.item.color,
|
color: element.color,
|
||||||
fontSize: this.item.size
|
fontSize: element.size
|
||||||
};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
index(newIndex) {
|
|
||||||
if (!this.context) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.context.index = newIndex;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.context = {
|
this.item.config.attachListeners();
|
||||||
layoutItem: this.item,
|
|
||||||
index: this.index
|
|
||||||
};
|
|
||||||
this.removeSelectable = this.openmct.selection.selectable(
|
|
||||||
this.$el, this.context, this.initSelect);
|
|
||||||
},
|
},
|
||||||
destroyed() {
|
destroyed() {
|
||||||
if (this.removeSelectable) {
|
this.item.config.removeListeners();
|
||||||
this.removeSelectable();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -33,9 +33,6 @@ export default function () {
|
|||||||
canView: function (domainObject) {
|
canView: function (domainObject) {
|
||||||
return domainObject.type === 'layout';
|
return domainObject.type === 'layout';
|
||||||
},
|
},
|
||||||
canEdit: function (domainObject) {
|
|
||||||
return domainObject.type === 'layout';
|
|
||||||
},
|
|
||||||
view: function (domainObject) {
|
view: function (domainObject) {
|
||||||
let component;
|
let component;
|
||||||
return {
|
return {
|
||||||
@@ -60,9 +57,7 @@ export default function () {
|
|||||||
getSelectionContext() {
|
getSelectionContext() {
|
||||||
return {
|
return {
|
||||||
item: domainObject,
|
item: domainObject,
|
||||||
addElement: component && component.$refs.displayLayout.addElement,
|
addElement: component && component.$refs.displayLayout.addElement
|
||||||
removeItem: component && component.$refs.displayLayout.removeItem,
|
|
||||||
orderItem: component && component.$refs.displayLayout.orderItem
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
destroy() {
|
destroy() {
|
||||||
|
|||||||
@@ -22,52 +22,47 @@
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="c-fl-container"
|
<div class="c-fl-container"
|
||||||
:style="[{'flex-basis': sizeString}]"
|
:style="[{'flex-basis': size}]"
|
||||||
:class="{'is-empty': !frames.length}">
|
:class="{'is-empty': frames.length === 1}">
|
||||||
<div class="c-fl-container__header"
|
<div class="c-fl-container__header icon-grippy-ew"
|
||||||
v-show="isEditing"
|
v-show="isEditing"
|
||||||
draggable="true"
|
draggable="true"
|
||||||
@dragstart="startContainerDrag">
|
@dragstart="startContainerDrag"
|
||||||
<span class="c-fl-container__size-indicator">{{ sizeString }}</span>
|
@dragend="stopContainerDrag">
|
||||||
|
<span class="c-fl-container__size-indicator">{{ size }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<drop-hint
|
|
||||||
class="c-fl-frame__drop-hint"
|
|
||||||
:index="-1"
|
|
||||||
:allow-drop="allowDrop"
|
|
||||||
@object-drop-to="moveOrCreateFrame">
|
|
||||||
</drop-hint>
|
|
||||||
|
|
||||||
<div class="c-fl-container__frames-holder">
|
<div class="c-fl-container__frames-holder">
|
||||||
<template
|
<div class="u-contents"
|
||||||
v-for="(frame, i) in frames">
|
v-for="(frame, i) in frames"
|
||||||
|
:key="i">
|
||||||
|
|
||||||
<frame-component
|
<frame-component
|
||||||
class="c-fl-container__frame"
|
class="c-fl-container__frame"
|
||||||
:key="frame.id"
|
:style="{
|
||||||
|
'flex-basis': `${frame.height}%`
|
||||||
|
}"
|
||||||
:frame="frame"
|
:frame="frame"
|
||||||
|
:size="frame.height"
|
||||||
:index="i"
|
:index="i"
|
||||||
:containerIndex="index">
|
:containerIndex="index"
|
||||||
|
:isEditing="isEditing"
|
||||||
|
:isDragging="isDragging"
|
||||||
|
@frame-drag-from="frameDragFrom"
|
||||||
|
@frame-drop-to="frameDropTo"
|
||||||
|
@delete-frame="promptBeforeDeletingFrame"
|
||||||
|
@add-container="addContainer">
|
||||||
</frame-component>
|
</frame-component>
|
||||||
|
|
||||||
<drop-hint
|
|
||||||
class="c-fl-frame__drop-hint"
|
|
||||||
:key="i"
|
|
||||||
:index="i"
|
|
||||||
:allowDrop="allowDrop"
|
|
||||||
@object-drop-to="moveOrCreateFrame">
|
|
||||||
</drop-hint>
|
|
||||||
|
|
||||||
<resize-handle
|
<resize-handle
|
||||||
v-if="(i !== frames.length - 1)"
|
v-if="i !== 0 && (i !== frames.length - 1)"
|
||||||
:key="i"
|
v-show="isEditing"
|
||||||
:index="i"
|
:index="i"
|
||||||
:orientation="rowsLayout ? 'horizontal' : 'vertical'"
|
:orientation="rowsLayout ? 'horizontal' : 'vertical'"
|
||||||
@init-move="startFrameResizing"
|
@init-move="startFrameResizing"
|
||||||
@move="frameResizing"
|
@move="frameResizing"
|
||||||
@end-move="endFrameResizing">
|
@end-move="endFrameResizing">
|
||||||
</resize-handle>
|
</resize-handle>
|
||||||
</template>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -76,98 +71,61 @@
|
|||||||
import FrameComponent from './frame.vue';
|
import FrameComponent from './frame.vue';
|
||||||
import Frame from '../utils/frame';
|
import Frame from '../utils/frame';
|
||||||
import ResizeHandle from './resizeHandle.vue';
|
import ResizeHandle from './resizeHandle.vue';
|
||||||
import DropHint from './dropHint.vue';
|
|
||||||
import isEditingMixin from '../mixins/isEditing';
|
|
||||||
|
|
||||||
|
const SNAP_TO_PERCENTAGE = 1;
|
||||||
const MIN_FRAME_SIZE = 5;
|
const MIN_FRAME_SIZE = 5;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
inject:['openmct'],
|
inject:['openmct', 'domainObject'],
|
||||||
props: ['container', 'index', 'rowsLayout'],
|
props: ['size', 'frames', 'index', 'isEditing', 'isDragging', 'rowsLayout'],
|
||||||
mixins: [isEditingMixin],
|
|
||||||
components: {
|
components: {
|
||||||
FrameComponent,
|
FrameComponent,
|
||||||
ResizeHandle,
|
ResizeHandle
|
||||||
DropHint
|
|
||||||
},
|
},
|
||||||
computed: {
|
data() {
|
||||||
frames() {
|
return {
|
||||||
return this.container.frames;
|
initialPos: 0,
|
||||||
},
|
frameIndex: 0,
|
||||||
sizeString() {
|
maxMoveSize: 0
|
||||||
return `${Math.round(this.container.size)}%`
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
allowDrop(event, index) {
|
frameDragFrom(frameIndex) {
|
||||||
if (event.dataTransfer.types.includes('openmct/domain-object-path')) {
|
this.$emit('frame-drag-from', this.index, frameIndex);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
let frameId = event.dataTransfer.getData('frameid'),
|
|
||||||
containerIndex = Number(event.dataTransfer.getData('containerIndex'));
|
|
||||||
|
|
||||||
if (!frameId) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (containerIndex === this.index) {
|
|
||||||
let frame = this.container.frames.filter((f) => f.id === frameId)[0],
|
|
||||||
framePos = this.container.frames.indexOf(frame);
|
|
||||||
|
|
||||||
if (index === -1) {
|
|
||||||
return framePos !== 0;
|
|
||||||
} else {
|
|
||||||
return framePos !== index && (framePos - 1) !== index
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
moveOrCreateFrame(insertIndex, event) {
|
frameDropTo(frameIndex, event) {
|
||||||
if (event.dataTransfer.types.includes('openmct/domain-object-path')) {
|
let domainObject = event.dataTransfer.getData('domainObject'),
|
||||||
// create frame using domain object
|
frameObject;
|
||||||
let domainObject = JSON.parse(event.dataTransfer.getData('openmct/domain-object-path'))[0];
|
|
||||||
this.$emit(
|
if (domainObject) {
|
||||||
'create-frame',
|
frameObject = new Frame(JSON.parse(domainObject));
|
||||||
this.index,
|
}
|
||||||
insertIndex,
|
|
||||||
domainObject.identifier
|
this.$emit('frame-drop-to', this.index, frameIndex, frameObject);
|
||||||
);
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
// move frame.
|
|
||||||
let frameId = event.dataTransfer.getData('frameid');
|
|
||||||
let containerIndex = Number(event.dataTransfer.getData('containerIndex'));
|
|
||||||
this.$emit(
|
|
||||||
'move-frame',
|
|
||||||
this.index,
|
|
||||||
insertIndex,
|
|
||||||
frameId,
|
|
||||||
containerIndex
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
startFrameResizing(index) {
|
startFrameResizing(index) {
|
||||||
let beforeFrame = this.frames[index],
|
let beforeFrame = this.frames[index],
|
||||||
afterFrame = this.frames[index + 1];
|
afterFrame = this.frames[index + 1];
|
||||||
|
|
||||||
this.maxMoveSize = beforeFrame.size + afterFrame.size;
|
this.maxMoveSize = beforeFrame.height + afterFrame.height;
|
||||||
},
|
},
|
||||||
frameResizing(index, delta, event) {
|
frameResizing(index, delta, event) {
|
||||||
let percentageMoved = Math.round(delta / this.getElSize() * 100),
|
|
||||||
|
let percentageMoved = (delta / this.getElSize(this.$el))*100,
|
||||||
beforeFrame = this.frames[index],
|
beforeFrame = this.frames[index],
|
||||||
afterFrame = this.frames[index + 1];
|
afterFrame = this.frames[index + 1];
|
||||||
|
|
||||||
beforeFrame.size = this.getFrameSize(beforeFrame.size + percentageMoved);
|
beforeFrame.height = this.snapToPercentage(beforeFrame.height + percentageMoved);
|
||||||
afterFrame.size = this.getFrameSize(afterFrame.size - percentageMoved);
|
afterFrame.height = this.snapToPercentage(afterFrame.height - percentageMoved);
|
||||||
},
|
},
|
||||||
endFrameResizing(index, event) {
|
endFrameResizing(index, event) {
|
||||||
this.persist();
|
this.persist();
|
||||||
},
|
},
|
||||||
getElSize() {
|
getElSize(el) {
|
||||||
if (this.rowsLayout) {
|
if (this.rowsLayout) {
|
||||||
return this.$el.offsetWidth;
|
return el.offsetWidth;
|
||||||
} else {
|
} else {
|
||||||
return this.$el.offsetHeight;
|
return el.offsetHeight;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getFrameSize(size) {
|
getFrameSize(size) {
|
||||||
@@ -179,23 +137,76 @@ export default {
|
|||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
snapToPercentage(value){
|
||||||
|
let rem = value % SNAP_TO_PERCENTAGE,
|
||||||
|
roundedValue;
|
||||||
|
|
||||||
|
if (rem < 0.5) {
|
||||||
|
roundedValue = Math.floor(value/SNAP_TO_PERCENTAGE)*SNAP_TO_PERCENTAGE;
|
||||||
|
} else {
|
||||||
|
roundedValue = Math.ceil(value/SNAP_TO_PERCENTAGE)*SNAP_TO_PERCENTAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.getFrameSize(roundedValue);
|
||||||
|
},
|
||||||
persist() {
|
persist() {
|
||||||
this.$emit('persist', this.index);
|
this.$emit('persist', this.index);
|
||||||
},
|
},
|
||||||
|
promptBeforeDeletingFrame(frameIndex) {
|
||||||
|
let deleteFrame = this.deleteFrame;
|
||||||
|
|
||||||
|
let prompt = this.openmct.overlays.dialog({
|
||||||
|
iconClass: 'alert',
|
||||||
|
message: `This action will remove ${this.frames[frameIndex].domainObject.name} from this Flexible Layout. Do you want to continue?`,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
label: 'Ok',
|
||||||
|
emphasis: 'true',
|
||||||
|
callback: function () {
|
||||||
|
deleteFrame(frameIndex);
|
||||||
|
prompt.dismiss();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Cancel',
|
||||||
|
callback: function () {
|
||||||
|
prompt.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
},
|
||||||
|
deleteFrame(frameIndex) {
|
||||||
|
this.frames.splice(frameIndex, 1);
|
||||||
|
this.$parent.recalculateOldFrameSize(this.frames);
|
||||||
|
this.persist();
|
||||||
|
},
|
||||||
|
deleteContainer() {
|
||||||
|
this.$emit('delete-container', this.index);
|
||||||
|
},
|
||||||
|
addContainer() {
|
||||||
|
this.$emit('add-container', this.index);
|
||||||
|
},
|
||||||
startContainerDrag(event) {
|
startContainerDrag(event) {
|
||||||
event.dataTransfer.setData('containerid', this.container.id);
|
event.stopPropagation();
|
||||||
|
this.$emit('start-container-drag', this.index);
|
||||||
|
},
|
||||||
|
stopContainerDrag(event) {
|
||||||
|
event.stopPropagation();
|
||||||
|
this.$emit('stop-container-drag');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
let context = {
|
let context = {
|
||||||
item: this.$parent.domainObject,
|
item: this.domainObject,
|
||||||
|
method: this.deleteContainer,
|
||||||
addContainer: this.addContainer,
|
addContainer: this.addContainer,
|
||||||
type: 'container',
|
index: this.index,
|
||||||
containerId: this.container.id
|
type: 'container'
|
||||||
}
|
}
|
||||||
|
|
||||||
this.unsubscribeSelection = this.openmct.selection.selectable(this.$el, context, false);
|
this.unsubscribeSelection = this.openmct.selection.selectable(this.$el, context, false);
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
this.unsubscribeSelection();
|
this.unsubscribeSelection();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-show="isValidTarget">
|
<div>
|
||||||
<div class="c-drop-hint c-drop-hint--always-show"
|
<div class="c-drop-hint c-drop-hint--always-show"
|
||||||
:class="{'is-mouse-over': isMouseOver}"
|
:class="{'is-mouse-over': isMouseOver}"
|
||||||
@dragenter="dragenter"
|
@dragenter="dragenter"
|
||||||
@@ -37,17 +37,10 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
props:{
|
props:['index'],
|
||||||
index: Number,
|
|
||||||
allowDrop: {
|
|
||||||
type: Function,
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
isMouseOver: false,
|
isMouseOver: false
|
||||||
isValidTarget: false
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@@ -58,23 +51,8 @@ export default {
|
|||||||
this.isMouseOver = false;
|
this.isMouseOver = false;
|
||||||
},
|
},
|
||||||
dropHandler(event) {
|
dropHandler(event) {
|
||||||
this.$emit('object-drop-to', this.index, event);
|
this.$emit('object-drop-to', event, this.index);
|
||||||
this.isValidTarget = false;
|
|
||||||
},
|
|
||||||
dragstart(event) {
|
|
||||||
this.isValidTarget = this.allowDrop(event, this.index);
|
|
||||||
},
|
|
||||||
dragend() {
|
|
||||||
this.isValidTarget = false;
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
document.addEventListener('dragstart', this.dragstart);
|
|
||||||
document.addEventListener('dragend', this.dragend);
|
|
||||||
},
|
|
||||||
destroyed() {
|
|
||||||
document.removeEventListener('dragstart', this.dragstart);
|
|
||||||
document.removeEventListener('dragend', this.dragend);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,25 +1,3 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* 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>
|
<template>
|
||||||
<div class="c-fl">
|
<div class="c-fl">
|
||||||
<div class="c-fl__empty"
|
<div class="c-fl__empty"
|
||||||
@@ -32,31 +10,40 @@
|
|||||||
'c-fl--rows': rowsLayout === true
|
'c-fl--rows': rowsLayout === true
|
||||||
}">
|
}">
|
||||||
|
|
||||||
<template v-for="(container, index) in containers">
|
<div class="u-contents"
|
||||||
|
v-for="(container, index) in containers"
|
||||||
|
:key="index">
|
||||||
|
|
||||||
<drop-hint
|
<drop-hint
|
||||||
class="c-fl-frame__drop-hint"
|
style="flex-basis: 15px;"
|
||||||
v-if="index === 0 && containers.length > 1"
|
v-if="index === 0 && containers.length > 1"
|
||||||
:key="index"
|
v-show="isContainerDragging"
|
||||||
:index="-1"
|
:index="-1"
|
||||||
:allow-drop="allowContainerDrop"
|
@object-drop-to="containerDropTo">
|
||||||
@object-drop-to="moveContainer">
|
|
||||||
</drop-hint>
|
</drop-hint>
|
||||||
|
|
||||||
<container-component
|
<container-component
|
||||||
class="c-fl__container"
|
class="c-fl__container"
|
||||||
:key="container.id"
|
ref="containerComponent"
|
||||||
:index="index"
|
:index="index"
|
||||||
:container="container"
|
:size="`${Math.round(container.width)}%`"
|
||||||
|
:frames="container.frames"
|
||||||
|
:isEditing="isEditing"
|
||||||
|
:isDragging="isDragging"
|
||||||
:rowsLayout="rowsLayout"
|
:rowsLayout="rowsLayout"
|
||||||
@move-frame="moveFrame"
|
@addFrame="addFrame"
|
||||||
@create-frame="createFrame"
|
@frame-drag-from="frameDragFromHandler"
|
||||||
@persist="persist">
|
@frame-drop-to="frameDropToHandler"
|
||||||
|
@persist="persist"
|
||||||
|
@delete-container="promptBeforeDeletingContainer"
|
||||||
|
@add-container="addContainer"
|
||||||
|
@start-container-drag="startContainerDrag"
|
||||||
|
@stop-container-drag="stopContainerDrag">
|
||||||
</container-component>
|
</container-component>
|
||||||
|
|
||||||
<resize-handle
|
<resize-handle
|
||||||
v-if="index !== (containers.length - 1)"
|
v-if="index !== (containers.length - 1)"
|
||||||
:key="index"
|
v-show="isEditing"
|
||||||
:index="index"
|
:index="index"
|
||||||
:orientation="rowsLayout ? 'vertical' : 'horizontal'"
|
:orientation="rowsLayout ? 'vertical' : 'horizontal'"
|
||||||
@init-move="startContainerResizing"
|
@init-move="startContainerResizing"
|
||||||
@@ -65,36 +52,19 @@
|
|||||||
</resize-handle>
|
</resize-handle>
|
||||||
|
|
||||||
<drop-hint
|
<drop-hint
|
||||||
class="c-fl-frame__drop-hint"
|
style="flex-basis: 15px;"
|
||||||
v-if="containers.length > 1"
|
v-if="containers.length > 1"
|
||||||
:key="index"
|
v-show="isContainerDragging"
|
||||||
:index="index"
|
:index="index"
|
||||||
:allowDrop="allowContainerDrop"
|
@object-drop-to="containerDropTo">
|
||||||
@object-drop-to="moveContainer">
|
|
||||||
</drop-hint>
|
</drop-hint>
|
||||||
</template>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import '~styles/sass-base';
|
@import '~styles/sass-base';
|
||||||
|
|
||||||
@mixin containerGrippy($headerSize, $dir) {
|
|
||||||
position: absolute;
|
|
||||||
$h: 6px;
|
|
||||||
$minorOffset: ($headerSize - $h) / 2;
|
|
||||||
$majorOffset: 35%;
|
|
||||||
content: '';
|
|
||||||
display: block;
|
|
||||||
position: absolute;
|
|
||||||
@include grippy($c: $editFrameSelectedMovebarColorFg, $dir: $dir);
|
|
||||||
@if $dir == 'x' {
|
|
||||||
top: $minorOffset; right: $majorOffset; bottom: $minorOffset; left: $majorOffset;
|
|
||||||
} @else {
|
|
||||||
top: $majorOffset; right: $minorOffset; bottom: $majorOffset; left: $minorOffset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.c-fl {
|
.c-fl {
|
||||||
@include abs();
|
@include abs();
|
||||||
@@ -116,6 +86,7 @@
|
|||||||
> * + * { margin-left: 1px; }
|
> * + * { margin-left: 1px; }
|
||||||
|
|
||||||
&[class*='--rows'] {
|
&[class*='--rows'] {
|
||||||
|
//@include test(blue, 0.1);
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
> * + * {
|
> * + * {
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
@@ -143,6 +114,7 @@
|
|||||||
/***************************************************** CONTAINERS */
|
/***************************************************** CONTAINERS */
|
||||||
$headerSize: 16px;
|
$headerSize: 16px;
|
||||||
|
|
||||||
|
border: 1px solid transparent;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
@@ -152,9 +124,9 @@
|
|||||||
flex-shrink: 1;
|
flex-shrink: 1;
|
||||||
|
|
||||||
&__header {
|
&__header {
|
||||||
// Only displayed when editing, controlled via JS
|
// Only displayed when editing
|
||||||
background: $editFrameMovebarColorBg;
|
background: $editSelectableColor;
|
||||||
color: $editFrameMovebarColorFg;
|
color: $editSelectableColorFg;
|
||||||
cursor: move;
|
cursor: move;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -162,8 +134,12 @@
|
|||||||
|
|
||||||
&:before {
|
&:before {
|
||||||
// Drag grippy
|
// Drag grippy
|
||||||
@include containerGrippy($headerSize, 'x');
|
font-size: 0.8em;
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
|
position: absolute;
|
||||||
|
left: 50%; top: 50%;
|
||||||
|
transform-origin: center;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,27 +159,19 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.is-editing & {
|
.is-editing & {
|
||||||
&:hover {
|
//background: $editCanvasColorBg;
|
||||||
.c-fl-container__header {
|
border-color: $editSelectableColor;
|
||||||
background: $editFrameHovMovebarColorBg;
|
|
||||||
color: $editFrameHovMovebarColorFg;
|
|
||||||
|
|
||||||
&:before {
|
&:hover {
|
||||||
opacity: .75;
|
border-color: $editSelectableColorHov;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&[s-selected] {
|
&[s-selected] {
|
||||||
border: $editFrameSelectedBorder;
|
border-color: $editSelectableColorSelected;
|
||||||
|
|
||||||
.c-fl-container__header {
|
.c-fl-container__header {
|
||||||
background:$editFrameSelectedMovebarColorBg;
|
background: $editSelectableColorSelected;
|
||||||
color: $editFrameSelectedMovebarColorFg;
|
color: $editSelectableColorSelectedFg;
|
||||||
&:before {
|
|
||||||
// Grippy
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -212,6 +180,11 @@
|
|||||||
// Frames get styled here because this is particular to their presence in this layout type
|
// Frames get styled here because this is particular to their presence in this layout type
|
||||||
.c-fl-frame {
|
.c-fl-frame {
|
||||||
@include browserPrefix(margin-collapse, collapse);
|
@include browserPrefix(margin-collapse, collapse);
|
||||||
|
margin: 1px;
|
||||||
|
|
||||||
|
//&__drag-wrapper {
|
||||||
|
// border: 1px solid $colorInteriorBorder; // Now handled by is-selectable
|
||||||
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
/****** ROWS LAYOUT */
|
/****** ROWS LAYOUT */
|
||||||
@@ -224,8 +197,8 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
&:before {
|
&:before {
|
||||||
// Grippy
|
// Drag grippy
|
||||||
@include containerGrippy($headerSize, 'y');
|
transform: rotate(90deg) translate(-50%, 50%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -248,6 +221,8 @@
|
|||||||
$dropHintSize: 15px;
|
$dropHintSize: 15px;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
// justify-content: stretch;
|
||||||
|
// align-items: stretch;
|
||||||
flex: 1 1;
|
flex: 1 1;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow: hidden; // Needed to allow frames to collapse when sized down
|
overflow: hidden; // Needed to allow frames to collapse when sized down
|
||||||
@@ -286,6 +261,7 @@
|
|||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
width: $size;
|
width: $size;
|
||||||
|
z-index: 2;
|
||||||
|
|
||||||
// Changed when layout is different, see below
|
// Changed when layout is different, see below
|
||||||
border-top-right-radius: $controlCr;
|
border-top-right-radius: $controlCr;
|
||||||
@@ -314,16 +290,37 @@
|
|||||||
|
|
||||||
&:before {
|
&:before {
|
||||||
// The visible resize line
|
// The visible resize line
|
||||||
background: $editUIColor;
|
background: $editColor;
|
||||||
content: '';
|
content: '';
|
||||||
display: block;
|
display: block;
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
min-height: $size; min-width: $size;
|
min-height: $size; min-width: $size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__grippy {
|
||||||
|
// Grippy element
|
||||||
|
$d: 4px;
|
||||||
|
$c: black;
|
||||||
|
$a: 0.9;
|
||||||
|
$d: 5px;
|
||||||
|
background: $editColor;
|
||||||
|
color: $editColorBg;
|
||||||
|
border-radius: $smallCr;
|
||||||
|
font-size: 0.8em;
|
||||||
|
height: $d;
|
||||||
|
width: $d * 10;
|
||||||
|
position: absolute;
|
||||||
|
left: 50%; top: 50%;
|
||||||
|
text-align: center;
|
||||||
|
transform-origin: center center;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
&.vertical {
|
&.vertical {
|
||||||
padding: $margin $size;
|
padding: $margin $size;
|
||||||
&:hover{
|
&:hover{
|
||||||
|
// padding: $marginHov 0;
|
||||||
cursor: row-resize;
|
cursor: row-resize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -331,15 +328,20 @@
|
|||||||
&.horizontal {
|
&.horizontal {
|
||||||
padding: $size $margin;
|
padding: $size $margin;
|
||||||
&:hover{
|
&:hover{
|
||||||
|
// padding: 0 $marginHov;
|
||||||
cursor: col-resize;
|
cursor: col-resize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[class*='grippy'] {
|
||||||
|
transform: translate(-50%) rotate(90deg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
transition: $transOut;
|
transition: $transOut;
|
||||||
&:before {
|
&:before {
|
||||||
// The visible resize line
|
// The visible resize line
|
||||||
background: $editUIColorHov;
|
background: $editColorHov;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -381,7 +383,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__drop-hint {
|
&__drop-hint {
|
||||||
flex: 1 0 100%;
|
flex: 1 1 100%;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -395,63 +397,31 @@
|
|||||||
<script>
|
<script>
|
||||||
import ContainerComponent from './container.vue';
|
import ContainerComponent from './container.vue';
|
||||||
import Container from '../utils/container';
|
import Container from '../utils/container';
|
||||||
import Frame from '../utils/frame';
|
|
||||||
import ResizeHandle from './resizeHandle.vue';
|
import ResizeHandle from './resizeHandle.vue';
|
||||||
import DropHint from './dropHint.vue';
|
import DropHint from './dropHint.vue';
|
||||||
import isEditingMixin from '../mixins/isEditing';
|
|
||||||
|
|
||||||
const MIN_CONTAINER_SIZE = 5;
|
const SNAP_TO_PERCENTAGE = 1,
|
||||||
|
MIN_CONTAINER_SIZE = 5;
|
||||||
// Resize items so that newItem fits proportionally (newItem must be an element
|
|
||||||
// of items). If newItem does not have a size or is sized at 100%, newItem will
|
|
||||||
// have size set to 1/n * 100, where n is the total number of items.
|
|
||||||
function sizeItems(items, newItem) {
|
|
||||||
if (items.length === 1) {
|
|
||||||
newItem.size = 100;
|
|
||||||
} else {
|
|
||||||
if (!newItem.size || newItem.size === 100) {
|
|
||||||
newItem.size = Math.round(100 / items.length);
|
|
||||||
}
|
|
||||||
let oldItems = items.filter(item => item !== newItem);
|
|
||||||
// Resize oldItems to fit inside remaining space;
|
|
||||||
let remainder = 100 - newItem.size;
|
|
||||||
oldItems.forEach((item) => {
|
|
||||||
item.size = Math.round(item.size * remainder / 100);
|
|
||||||
});
|
|
||||||
// Ensure items add up to 100 in case of rounding error.
|
|
||||||
let total = items.reduce((t, item) => t + item.size, 0);
|
|
||||||
let excess = Math.round(100 - total);
|
|
||||||
oldItems[oldItems.length - 1].size += excess;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scales items proportionally so total is equal to 100. Assumes that an item
|
|
||||||
// was removed from array.
|
|
||||||
function sizeToFill(items) {
|
|
||||||
if (items.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let oldTotal = items.reduce((total, item) => total + item.size, 0);
|
|
||||||
items.forEach((item) => {
|
|
||||||
item.size = Math.round(item.size * 100 / oldTotal);
|
|
||||||
});
|
|
||||||
// Ensure items add up to 100 in case of rounding error.
|
|
||||||
let total = items.reduce((t, item) => t + item.size, 0);
|
|
||||||
let excess = Math.round(100 - total);
|
|
||||||
items[items.length - 1].size += excess;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
inject: ['openmct', 'layoutObject'],
|
inject: ['openmct', 'domainObject'],
|
||||||
mixins: [isEditingMixin],
|
|
||||||
components: {
|
components: {
|
||||||
ContainerComponent,
|
ContainerComponent,
|
||||||
ResizeHandle,
|
ResizeHandle,
|
||||||
DropHint
|
DropHint
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
let containers = this.domainObject.configuration.containers,
|
||||||
|
rowsLayout = this.domainObject.configuration.rowsLayout;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
domainObject: this.layoutObject
|
containers: containers,
|
||||||
|
dragFrom: [],
|
||||||
|
isEditing: false,
|
||||||
|
isDragging: false,
|
||||||
|
isContainerDragging: false,
|
||||||
|
rowsLayout: rowsLayout,
|
||||||
|
maxMoveSize: 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -461,105 +431,144 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
return 'Columns'
|
return 'Columns'
|
||||||
}
|
}
|
||||||
},
|
|
||||||
containers() {
|
|
||||||
return this.domainObject.configuration.containers;
|
|
||||||
},
|
|
||||||
rowsLayout() {
|
|
||||||
return this.domainObject.configuration.rowsLayout;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
areAllContainersEmpty() {
|
areAllContainersEmpty() {
|
||||||
return !!!this.containers.filter(container => container.frames.length).length;
|
return !!!this.containers.filter(container => container.frames.length > 1).length;
|
||||||
},
|
},
|
||||||
addContainer() {
|
addContainer() {
|
||||||
let container = new Container();
|
let newSize = 100/(this.containers.length+1);
|
||||||
|
|
||||||
|
let container = new Container(newSize)
|
||||||
|
|
||||||
|
this.recalculateContainerSize(newSize);
|
||||||
|
|
||||||
this.containers.push(container);
|
this.containers.push(container);
|
||||||
sizeItems(this.containers, container);
|
|
||||||
this.persist();
|
|
||||||
},
|
},
|
||||||
deleteContainer(containerId) {
|
recalculateContainerSize(newSize) {
|
||||||
let container = this.containers.filter(c => c.id === containerId)[0];
|
this.containers.forEach((container) => {
|
||||||
let containerIndex = this.containers.indexOf(container);
|
container.width = newSize;
|
||||||
this.containers.splice(containerIndex, 1);
|
});
|
||||||
sizeToFill(this.containers);
|
|
||||||
this.persist();
|
|
||||||
},
|
},
|
||||||
moveFrame(toContainerIndex, toFrameIndex, frameId, fromContainerIndex) {
|
recalculateNewFrameSize(multFactor, framesArray){
|
||||||
let toContainer = this.containers[toContainerIndex];
|
framesArray.forEach((frame, index) => {
|
||||||
let fromContainer = this.containers[fromContainerIndex];
|
if (index === 0) {
|
||||||
let frame = fromContainer.frames.filter(f => f.id === frameId)[0];
|
return;
|
||||||
let fromIndex = fromContainer.frames.indexOf(frame);
|
}
|
||||||
fromContainer.frames.splice(fromIndex, 1);
|
let frameSize = frame.height
|
||||||
sizeToFill(fromContainer.frames);
|
frame.height = this.snapToPercentage(multFactor * frameSize);
|
||||||
toContainer.frames.splice(toFrameIndex + 1, 0, frame);
|
});
|
||||||
sizeItems(toContainer.frames, frame);
|
|
||||||
this.persist();
|
|
||||||
},
|
},
|
||||||
createFrame(containerIndex, insertFrameIndex, objectIdentifier) {
|
recalculateOldFrameSize(framesArray) {
|
||||||
let frame = new Frame(objectIdentifier);
|
let totalRemainingSum = framesArray.map((frame,i) => {
|
||||||
let container = this.containers[containerIndex];
|
if (i !== 0) {
|
||||||
container.frames.splice(insertFrameIndex + 1, 0, frame);
|
return frame.height
|
||||||
sizeItems(container.frames, frame);
|
} else {
|
||||||
this.persist();
|
return 0;
|
||||||
|
}
|
||||||
|
}).reduce((a, c) => a + c);
|
||||||
|
|
||||||
|
framesArray.forEach((frame, index) => {
|
||||||
|
|
||||||
|
if (index === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (framesArray.length === 2) {
|
||||||
|
|
||||||
|
frame.height = 100;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
let newSize = frame.height + ((frame.height / totalRemainingSum) * (100 - totalRemainingSum));
|
||||||
|
frame.height = this.snapToPercentage(newSize);
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
deleteFrame(frameId) {
|
addFrame(frame, index) {
|
||||||
let container = this.containers
|
this.containers[index].addFrame(frame);
|
||||||
.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);
|
|
||||||
container.frames.splice(frameIndex, 1);
|
|
||||||
sizeToFill(container.frames);
|
|
||||||
this.persist(containerIndex);
|
|
||||||
},
|
},
|
||||||
allowContainerDrop(event, index) {
|
frameDragFromHandler(containerIndex, frameIndex) {
|
||||||
if (!event.dataTransfer.types.includes('containerid')) {
|
this.dragFrom = [containerIndex, frameIndex];
|
||||||
return false;
|
},
|
||||||
|
frameDropToHandler(containerIndex, frameIndex, frameObject) {
|
||||||
|
let newContainer = this.containers[containerIndex];
|
||||||
|
|
||||||
|
this.isDragging = false;
|
||||||
|
|
||||||
|
if (!frameObject) {
|
||||||
|
frameObject = this.containers[this.dragFrom[0]].frames.splice(this.dragFrom[1], 1)[0];
|
||||||
|
this.recalculateOldFrameSize(this.containers[this.dragFrom[0]].frames);
|
||||||
}
|
}
|
||||||
|
|
||||||
let containerId = event.dataTransfer.getData('containerid'),
|
if (!frameObject.height) {
|
||||||
container = this.containers.filter((c) => c.id === containerId)[0],
|
frameObject.height = 100 / Math.max(newContainer.frames.length - 1, 1);
|
||||||
containerPos = this.containers.indexOf(container);
|
|
||||||
|
|
||||||
if (index === -1) {
|
|
||||||
return containerPos !== 0;
|
|
||||||
} else {
|
|
||||||
return containerPos !== index && (containerPos - 1) !== index
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
newContainer.frames.splice((frameIndex + 1), 0, frameObject);
|
||||||
|
|
||||||
|
let newTotalHeight = newContainer.frames.reduce((total, frame) => {
|
||||||
|
let num = Number(frame.height);
|
||||||
|
|
||||||
|
if(isNaN(num)) {
|
||||||
|
return total;
|
||||||
|
} else {
|
||||||
|
return total + num;
|
||||||
|
}
|
||||||
|
},0);
|
||||||
|
let newMultFactor = 100 / newTotalHeight;
|
||||||
|
|
||||||
|
this.recalculateNewFrameSize(newMultFactor, newContainer.frames);
|
||||||
|
|
||||||
|
this.persist();
|
||||||
},
|
},
|
||||||
persist(index){
|
persist(index){
|
||||||
if (index) {
|
if (index) {
|
||||||
this.openmct.objects.mutate(this.domainObject, `configuration.containers[${index}]`, this.containers[index]);
|
this.openmct.objects.mutate(this.domainObject, `.configuration.containers[${index}]`, this.containers[index]);
|
||||||
} else {
|
} else {
|
||||||
this.openmct.objects.mutate(this.domainObject, 'configuration.containers', this.containers);
|
this.openmct.objects.mutate(this.domainObject, '.configuration.containers', this.containers);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
isEditingHandler(isEditing) {
|
||||||
|
this.isEditing = isEditing;
|
||||||
|
|
||||||
|
if (this.isEditing) {
|
||||||
|
this.$el.click(); //force selection of flexible-layout for toolbar
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.isDragging && isEditing === false) {
|
||||||
|
this.isDragging = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dragstartHandler() {
|
||||||
|
if (this.isEditing) {
|
||||||
|
this.isDragging = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dragendHandler() {
|
||||||
|
this.isDragging = false;
|
||||||
|
},
|
||||||
startContainerResizing(index) {
|
startContainerResizing(index) {
|
||||||
let beforeContainer = this.containers[index],
|
let beforeContainer = this.containers[index],
|
||||||
afterContainer = this.containers[index + 1];
|
afterContainer = this.containers[index + 1];
|
||||||
|
|
||||||
this.maxMoveSize = beforeContainer.size + afterContainer.size;
|
this.maxMoveSize = beforeContainer.width + afterContainer.width;
|
||||||
},
|
},
|
||||||
containerResizing(index, delta, event) {
|
containerResizing(index, delta, event) {
|
||||||
let percentageMoved = Math.round(delta / this.getElSize() * 100),
|
let percentageMoved = (delta/this.getElSize(this.$el))*100,
|
||||||
beforeContainer = this.containers[index],
|
beforeContainer = this.containers[index],
|
||||||
afterContainer = this.containers[index + 1];
|
afterContainer = this.containers[index + 1];
|
||||||
|
|
||||||
beforeContainer.size = this.getContainerSize(beforeContainer.size + percentageMoved);
|
beforeContainer.width = this.getContainerSize(this.snapToPercentage(beforeContainer.width + percentageMoved));
|
||||||
afterContainer.size = this.getContainerSize(afterContainer.size - percentageMoved);
|
afterContainer.width = this.getContainerSize(this.snapToPercentage(afterContainer.width - percentageMoved));
|
||||||
},
|
},
|
||||||
endContainerResizing(event) {
|
endContainerResizing(event) {
|
||||||
this.persist();
|
this.persist();
|
||||||
},
|
},
|
||||||
getElSize() {
|
getElSize(el) {
|
||||||
if (this.rowsLayout) {
|
if (this.rowsLayout) {
|
||||||
return this.$el.offsetHeight;
|
return el.offsetHeight;
|
||||||
} else {
|
} else {
|
||||||
return this.$el.offsetWidth;
|
return el.offsetWidth;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getContainerSize(size) {
|
getContainerSize(size) {
|
||||||
@@ -571,19 +580,80 @@ export default {
|
|||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
updateDomainObject(newDomainObject) {
|
snapToPercentage(value) {
|
||||||
this.domainObject = newDomainObject;
|
let rem = value % SNAP_TO_PERCENTAGE,
|
||||||
},
|
roundedValue;
|
||||||
moveContainer(toIndex, event) {
|
|
||||||
let containerId = event.dataTransfer.getData('containerid');
|
if (rem < 0.5) {
|
||||||
let container = this.containers.filter(c => c.id === containerId)[0];
|
roundedValue = Math.floor(value/SNAP_TO_PERCENTAGE)*SNAP_TO_PERCENTAGE;
|
||||||
let fromIndex = this.containers.indexOf(container);
|
|
||||||
this.containers.splice(fromIndex, 1);
|
|
||||||
if (fromIndex > toIndex) {
|
|
||||||
this.containers.splice(toIndex + 1, 0, container);
|
|
||||||
} else {
|
} else {
|
||||||
this.containers.splice(toIndex, 0, container);
|
roundedValue = Math.ceil(value/SNAP_TO_PERCENTAGE)*SNAP_TO_PERCENTAGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return roundedValue;
|
||||||
|
},
|
||||||
|
toggleLayoutDirection(v) {
|
||||||
|
this.rowsLayout = v;
|
||||||
|
},
|
||||||
|
promptBeforeDeletingContainer(containerIndex) {
|
||||||
|
let deleteContainer = this.deleteContainer;
|
||||||
|
|
||||||
|
let prompt = this.openmct.overlays.dialog({
|
||||||
|
iconClass: 'alert',
|
||||||
|
message: `This action will permanently delete container ${containerIndex + 1} from this Flexible Layout`,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
label: 'Ok',
|
||||||
|
emphasis: 'true',
|
||||||
|
callback: function () {
|
||||||
|
deleteContainer(containerIndex);
|
||||||
|
prompt.dismiss();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Cancel',
|
||||||
|
callback: function () {
|
||||||
|
prompt.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
},
|
||||||
|
deleteContainer(containerIndex) {
|
||||||
|
this.containers.splice(containerIndex, 1);
|
||||||
|
|
||||||
|
this.recalculateContainerSize(100/this.containers.length);
|
||||||
|
this.persist();
|
||||||
|
},
|
||||||
|
addContainer(containerIndex) {
|
||||||
|
let newContainer = new Container();
|
||||||
|
|
||||||
|
if (typeof containerIndex === 'number') {
|
||||||
|
this.containers.splice(containerIndex+1, 0, newContainer);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
this.containers.push(newContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.recalculateContainerSize(100/this.containers.length);
|
||||||
|
this.persist();
|
||||||
|
},
|
||||||
|
startContainerDrag(index) {
|
||||||
|
this.isContainerDragging = true;
|
||||||
|
this.containerDragFrom = index;
|
||||||
|
},
|
||||||
|
stopContainerDrag() {
|
||||||
|
this.isContainerDragging = false;
|
||||||
|
},
|
||||||
|
containerDropTo(event, index) {
|
||||||
|
let fromContainer = this.containers.splice(this.containerDragFrom, 1)[0];
|
||||||
|
|
||||||
|
if (index === -1) {
|
||||||
|
this.containers.unshift(fromContainer);
|
||||||
|
} else {
|
||||||
|
this.containers.splice(index, 0, fromContainer);
|
||||||
|
}
|
||||||
|
|
||||||
this.persist();
|
this.persist();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -592,17 +662,23 @@ export default {
|
|||||||
let context = {
|
let context = {
|
||||||
item: this.domainObject,
|
item: this.domainObject,
|
||||||
addContainer: this.addContainer,
|
addContainer: this.addContainer,
|
||||||
deleteContainer: this.deleteContainer,
|
|
||||||
deleteFrame: this.deleteFrame,
|
|
||||||
type: 'flexible-layout'
|
type: 'flexible-layout'
|
||||||
}
|
}
|
||||||
|
|
||||||
this.unsubscribeSelection = this.openmct.selection.selectable(this.$el, context, true);
|
this.unsubscribeSelection = this.openmct.selection.selectable(this.$el, context, true);
|
||||||
this.unobserve = this.openmct.objects.observe(this.domainObject, '*', this.updateDomainObject);
|
|
||||||
|
this.openmct.objects.observe(this.domainObject, 'configuration.rowsLayout', this.toggleLayoutDirection);
|
||||||
|
this.openmct.editor.on('isEditing', this.isEditingHandler);
|
||||||
|
|
||||||
|
document.addEventListener('dragstart', this.dragstartHandler);
|
||||||
|
document.addEventListener('dragend', this.dragendHandler);
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
this.unsubscribeSelection();
|
this.unsubscribeSelection();
|
||||||
this.unobserve();
|
|
||||||
|
this.openmct.editor.off('isEditing', this.isEditingHandler);
|
||||||
|
document.removeEventListener('dragstart', this.dragstartHandler);
|
||||||
|
document.removeEventListener('dragend', this.dragendHandler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -22,82 +22,103 @@
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="c-fl-frame"
|
<div class="c-fl-frame"
|
||||||
:style="{
|
:class="{
|
||||||
'flex-basis': `${frame.size}%`
|
'is-dragging': isDragging,
|
||||||
}">
|
[frame.cssClass]: true
|
||||||
|
}"
|
||||||
|
@dragstart="initDrag"
|
||||||
|
@drag="continueDrag">
|
||||||
|
|
||||||
<div class="c-frame c-fl-frame__drag-wrapper is-selectable u-inspectable is-moveable"
|
<div class="c-frame c-fl-frame__drag-wrapper is-selectable is-moveable"
|
||||||
|
:class="{'no-frame': noFrame}"
|
||||||
draggable="true"
|
draggable="true"
|
||||||
@dragstart="initDrag"
|
ref="frame"
|
||||||
ref="frame">
|
v-if="frame.domainObject">
|
||||||
|
|
||||||
<object-frame
|
<frame-header
|
||||||
v-if="domainObject"
|
v-if="index !== 0"
|
||||||
:domain-object="domainObject"
|
ref="dragObject"
|
||||||
:object-path="objectPath"
|
class="c-fl-frame__header"
|
||||||
:has-frame="hasFrame">
|
:domainObject="frame.domainObject">
|
||||||
</object-frame>
|
</frame-header>
|
||||||
|
|
||||||
|
<object-view
|
||||||
|
class="c-fl-frame__object-view"
|
||||||
|
:object="frame.domainObject">
|
||||||
|
</object-view>
|
||||||
|
|
||||||
<div class="c-fl-frame__size-indicator"
|
<div class="c-fl-frame__size-indicator"
|
||||||
v-if="isEditing"
|
v-if="isEditing"
|
||||||
v-show="frame.size && frame.size < 100">
|
v-show="frame.height && frame.height < 100">
|
||||||
{{frame.size}}%
|
{{frame.height}}%
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<drop-hint
|
||||||
|
v-show="isEditing && isDragging"
|
||||||
|
class="c-fl-frame__drop-hint"
|
||||||
|
:class="{'is-dragging': isDragging}"
|
||||||
|
@object-drop-to="dropHandler">
|
||||||
|
</drop-hint>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import ObjectView from '../../../ui/components/layout/ObjectView.vue';
|
||||||
|
import DropHint from './dropHint.vue';
|
||||||
import ResizeHandle from './resizeHandle.vue';
|
import ResizeHandle from './resizeHandle.vue';
|
||||||
import ObjectFrame from '../../../ui/components/ObjectFrame.vue';
|
import FrameHeader from '../../../ui/components/utils/frameHeader.vue';
|
||||||
import isEditingMixin from '../mixins/isEditing';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
inject: ['openmct'],
|
inject: ['openmct', 'domainObject'],
|
||||||
props: ['frame', 'index', 'containerIndex'],
|
props: ['frame', 'index', 'containerIndex', 'isEditing', 'isDragging'],
|
||||||
mixins: [isEditingMixin],
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
domainObject: undefined,
|
noFrame: this.frame.noFrame
|
||||||
objectPath: undefined
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
|
ObjectView,
|
||||||
|
DropHint,
|
||||||
ResizeHandle,
|
ResizeHandle,
|
||||||
ObjectFrame
|
FrameHeader
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
hasFrame() {
|
|
||||||
return !this.frame.noFrame;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
setDomainObject(object) {
|
|
||||||
console.log('setting object!');
|
|
||||||
this.domainObject = object;
|
|
||||||
this.objectPath = [object];
|
|
||||||
this.setSelection();
|
|
||||||
},
|
|
||||||
setSelection() {
|
|
||||||
let context = {
|
|
||||||
item: this.domainObject,
|
|
||||||
addContainer: this.addContainer,
|
|
||||||
type: 'frame',
|
|
||||||
frameId: this.frame.id
|
|
||||||
};
|
|
||||||
|
|
||||||
this.unsubscribeSelection = this.openmct.selection.selectable(this.$refs.frame, context, false);
|
|
||||||
},
|
|
||||||
initDrag(event) {
|
initDrag(event) {
|
||||||
event.dataTransfer.setData('frameid', this.frame.id);
|
this.$emit('frame-drag-from', this.index);
|
||||||
event.dataTransfer.setData('containerIndex', this.containerIndex);
|
},
|
||||||
|
dropHandler(event) {
|
||||||
|
this.$emit('frame-drop-to', this.index, event);
|
||||||
|
},
|
||||||
|
continueDrag(event) {
|
||||||
|
if (!this.isDragging) {
|
||||||
|
this.isDragging = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
deleteFrame() {
|
||||||
|
this.$emit('delete-frame', this.index);
|
||||||
|
},
|
||||||
|
addContainer() {
|
||||||
|
this.$emit('add-container');
|
||||||
|
},
|
||||||
|
toggleFrame(v) {
|
||||||
|
this.noFrame = v;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
if (this.frame.domainObjectIdentifier) {
|
|
||||||
this.openmct.objects.get(this.frame.domainObjectIdentifier).then((object)=>{
|
if (this.frame.domainObject.identifier) {
|
||||||
this.setDomainObject(object);
|
let context = {
|
||||||
});
|
item: this.frame.domainObject,
|
||||||
|
method: this.deleteFrame,
|
||||||
|
addContainer: this.addContainer,
|
||||||
|
type: 'frame',
|
||||||
|
index: this.index
|
||||||
|
}
|
||||||
|
|
||||||
|
this.unsubscribeSelection = this.openmct.selection.selectable(this.$refs.frame, context, false);
|
||||||
|
|
||||||
|
this.openmct.objects.observe(this.domainObject, `configuration.containers[${this.containerIndex}].frames[${this.index}].noFrame`, this.toggleFrame);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
|
|||||||
@@ -23,21 +23,16 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="c-fl-frame__resize-handle"
|
<div class="c-fl-frame__resize-handle"
|
||||||
:class="[orientation]"
|
:class="[orientation]"
|
||||||
v-show="isEditing && !isDragging"
|
|
||||||
@mousedown="mousedown">
|
@mousedown="mousedown">
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import isEditingMixin from '../mixins/isEditing';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['orientation', 'index'],
|
props: ['orientation', 'index'],
|
||||||
mixins: [isEditingMixin],
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
initialPos: 0,
|
initialPos: 0
|
||||||
isDragging: false,
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@@ -52,18 +47,7 @@ export default {
|
|||||||
mousemove(event) {
|
mousemove(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
let elSize, mousePos, delta;
|
let delta = this.getMousePosition(event) - this.getElSizeFromRect(this.$el);
|
||||||
|
|
||||||
if (this.orientation === 'horizontal') {
|
|
||||||
elSize = this.$el.getBoundingClientRect().x;
|
|
||||||
mousePos = event.clientX;
|
|
||||||
} else {
|
|
||||||
elSize = this.$el.getBoundingClientRect().y;
|
|
||||||
mousePos = event.clientY;
|
|
||||||
}
|
|
||||||
|
|
||||||
delta = mousePos - elSize;
|
|
||||||
|
|
||||||
this.$emit('move', this.index, delta, event);
|
this.$emit('move', this.index, delta, event);
|
||||||
},
|
},
|
||||||
mouseup(event) {
|
mouseup(event) {
|
||||||
@@ -72,20 +56,20 @@ export default {
|
|||||||
document.body.removeEventListener('mousemove', this.mousemove);
|
document.body.removeEventListener('mousemove', this.mousemove);
|
||||||
document.body.removeEventListener('mouseup', this.mouseup);
|
document.body.removeEventListener('mouseup', this.mouseup);
|
||||||
},
|
},
|
||||||
setDragging(event) {
|
getMousePosition(event) {
|
||||||
this.isDragging = true;
|
if (this.orientation === 'horizontal') {
|
||||||
|
return event.clientX;
|
||||||
|
} else {
|
||||||
|
return event.clientY;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getElSizeFromRect(el) {
|
||||||
|
if (this.orientation === 'horizontal') {
|
||||||
|
return el.getBoundingClientRect().x;
|
||||||
|
} else {
|
||||||
|
return el.getBoundingClientRect().y;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
unsetDragging(event) {
|
|
||||||
this.isDragging = false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
document.addEventListener('dragstart', this.setDragging);
|
|
||||||
document.addEventListener('dragend', this.unsetDragging);
|
|
||||||
},
|
|
||||||
destroyed() {
|
|
||||||
document.removeEventListener('dragstart', this.setDragging);
|
|
||||||
document.removeEventListener('dragend', this.unsetDragging);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -35,9 +35,6 @@ define([
|
|||||||
canView: function (domainObject) {
|
canView: function (domainObject) {
|
||||||
return domainObject.type === 'flexible-layout';
|
return domainObject.type === 'flexible-layout';
|
||||||
},
|
},
|
||||||
canEdit: function (domainObject) {
|
|
||||||
return domainObject.type === 'flexible-layout';
|
|
||||||
},
|
|
||||||
view: function (domainObject) {
|
view: function (domainObject) {
|
||||||
let component;
|
let component;
|
||||||
|
|
||||||
@@ -49,7 +46,7 @@ define([
|
|||||||
},
|
},
|
||||||
provide: {
|
provide: {
|
||||||
openmct,
|
openmct,
|
||||||
layoutObject: domainObject
|
domainObject
|
||||||
},
|
},
|
||||||
el: element,
|
el: element,
|
||||||
template: '<flexible-layout-component></flexible-layout-component>'
|
template: '<flexible-layout-component></flexible-layout-component>'
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
export default {
|
|
||||||
inject: ['openmct'],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
isEditing: this.openmct.editor.isEditing()
|
|
||||||
};
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.openmct.editor.on('isEditing', this.toggleEditing);
|
|
||||||
},
|
|
||||||
destroyed() {
|
|
||||||
this.openmct.editor.off('isEditing', this.toggleEditing);
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
toggleEditing(value) {
|
|
||||||
this.isEditing = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -22,12 +22,10 @@
|
|||||||
|
|
||||||
define([
|
define([
|
||||||
'./flexibleLayoutViewProvider',
|
'./flexibleLayoutViewProvider',
|
||||||
'./utils/container',
|
'./utils/container'
|
||||||
'./toolbarProvider'
|
|
||||||
], function (
|
], function (
|
||||||
FlexibleLayoutViewProvider,
|
FlexibleLayoutViewProvider,
|
||||||
Container,
|
Container
|
||||||
ToolBarProvider
|
|
||||||
) {
|
) {
|
||||||
return function plugin() {
|
return function plugin() {
|
||||||
|
|
||||||
@@ -44,13 +42,122 @@ define([
|
|||||||
containers: [new Container.default(50), new Container.default(50)],
|
containers: [new Container.default(50), new Container.default(50)],
|
||||||
rowsLayout: false
|
rowsLayout: false
|
||||||
};
|
};
|
||||||
domainObject.composition = [];
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let toolbar = ToolBarProvider.default(openmct);
|
openmct.toolbars.addProvider({
|
||||||
|
name: "Flexible Layout Toolbar",
|
||||||
|
key: "flex-layout",
|
||||||
|
description: "A toolbar for objects inside a Flexible Layout.",
|
||||||
|
forSelection: function (selection) {
|
||||||
|
let context = selection[0].context;
|
||||||
|
|
||||||
openmct.toolbars.addProvider(toolbar);
|
return (openmct.editor.isEditing() && context && context.type &&
|
||||||
|
(context.type === 'flexible-layout' || context.type === 'container' || context.type === 'frame'));
|
||||||
|
},
|
||||||
|
toolbar: function (selection) {
|
||||||
|
|
||||||
|
let primary = selection[0],
|
||||||
|
parent = selection[1],
|
||||||
|
deleteFrame,
|
||||||
|
toggleContainer,
|
||||||
|
deleteContainer,
|
||||||
|
addContainer,
|
||||||
|
toggleFrame,
|
||||||
|
separator;
|
||||||
|
|
||||||
|
addContainer = {
|
||||||
|
control: "button",
|
||||||
|
domainObject: parent ? parent.context.item : primary.context.item,
|
||||||
|
method: parent ? parent.context.addContainer : primary.context.addContainer,
|
||||||
|
key: "add",
|
||||||
|
icon: "icon-plus-in-rect",
|
||||||
|
title: 'Add Container'
|
||||||
|
};
|
||||||
|
|
||||||
|
separator = {
|
||||||
|
control: "separator",
|
||||||
|
domainObject: selection[0].context.item,
|
||||||
|
key: "separator"
|
||||||
|
};
|
||||||
|
|
||||||
|
toggleContainer = {
|
||||||
|
control: 'toggle-button',
|
||||||
|
key: 'toggle-layout',
|
||||||
|
domainObject: parent ? parent.context.item : primary.context.item,
|
||||||
|
property: 'configuration.rowsLayout',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
value: false,
|
||||||
|
icon: 'icon-columns',
|
||||||
|
title: 'Columns'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: true,
|
||||||
|
icon: 'icon-rows',
|
||||||
|
title: 'Rows'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
if (primary.context.type === 'frame') {
|
||||||
|
|
||||||
|
deleteFrame = {
|
||||||
|
control: "button",
|
||||||
|
domainObject: primary.context.item,
|
||||||
|
method: primary.context.method,
|
||||||
|
key: "remove",
|
||||||
|
icon: "icon-trash",
|
||||||
|
title: "Remove Frame"
|
||||||
|
};
|
||||||
|
toggleFrame = {
|
||||||
|
control: "toggle-button",
|
||||||
|
domainObject: parent.context.item,
|
||||||
|
property: `configuration.containers[${parent.context.index}].frames[${primary.context.index}].noFrame`,
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
value: true,
|
||||||
|
icon: 'icon-frame-hide',
|
||||||
|
title: "Hide frame"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: false,
|
||||||
|
icon: 'icon-frame-show',
|
||||||
|
title: "Show frame"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} else if (primary.context.type === 'container') {
|
||||||
|
|
||||||
|
deleteContainer = {
|
||||||
|
control: "button",
|
||||||
|
domainObject: primary.context.item,
|
||||||
|
method: primary.context.method,
|
||||||
|
key: "remove",
|
||||||
|
icon: "icon-trash",
|
||||||
|
title: "Remove Container"
|
||||||
|
};
|
||||||
|
|
||||||
|
} else if (primary.context.type === 'flexible-layout') {
|
||||||
|
|
||||||
|
addContainer = {
|
||||||
|
control: "button",
|
||||||
|
domainObject: primary.context.item,
|
||||||
|
method: primary.context.addContainer,
|
||||||
|
key: "add",
|
||||||
|
icon: "icon-plus-in-rect",
|
||||||
|
title: 'Add Container'
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
let toolbar = [toggleContainer, addContainer, toggleFrame, separator, deleteFrame, deleteContainer];
|
||||||
|
|
||||||
|
return toolbar.filter(button => button !== undefined);
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,215 +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.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
function ToolbarProvider(openmct) {
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: "Flexible Layout Toolbar",
|
|
||||||
key: "flex-layout",
|
|
||||||
description: "A toolbar for objects inside a Flexible Layout.",
|
|
||||||
forSelection: function (selection) {
|
|
||||||
let context = selection[0].context;
|
|
||||||
|
|
||||||
return (openmct.editor.isEditing() && context && context.type &&
|
|
||||||
(context.type === 'flexible-layout' || context.type === 'container' || context.type === 'frame'));
|
|
||||||
},
|
|
||||||
toolbar: function (selection) {
|
|
||||||
|
|
||||||
let primary = selection[0],
|
|
||||||
secondary = selection[1],
|
|
||||||
tertiary = selection[2],
|
|
||||||
deleteFrame,
|
|
||||||
toggleContainer,
|
|
||||||
deleteContainer,
|
|
||||||
addContainer,
|
|
||||||
toggleFrame,
|
|
||||||
separator;
|
|
||||||
|
|
||||||
separator = {
|
|
||||||
control: "separator",
|
|
||||||
domainObject: selection[0].context.item,
|
|
||||||
key: "separator"
|
|
||||||
};
|
|
||||||
|
|
||||||
toggleContainer = {
|
|
||||||
control: 'toggle-button',
|
|
||||||
key: 'toggle-layout',
|
|
||||||
domainObject: secondary ? secondary.context.item : primary.context.item,
|
|
||||||
property: 'configuration.rowsLayout',
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
value: true,
|
|
||||||
icon: 'icon-columns',
|
|
||||||
title: 'Columns layout'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: false,
|
|
||||||
icon: 'icon-rows',
|
|
||||||
title: 'Rows layout'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
if (primary.context.type === 'frame') {
|
|
||||||
let frameId = primary.context.frameId;
|
|
||||||
let layoutObject = tertiary.context.item;
|
|
||||||
let containers = layoutObject
|
|
||||||
.configuration
|
|
||||||
.containers;
|
|
||||||
let container = containers
|
|
||||||
.filter(c => c.frames.some(f => f.id === frameId))[0];
|
|
||||||
let frame = container
|
|
||||||
.frames
|
|
||||||
.filter((f => f.id === frameId))[0];
|
|
||||||
let containerIndex = containers.indexOf(container);
|
|
||||||
let frameIndex = container.frames.indexOf(frame);
|
|
||||||
|
|
||||||
deleteFrame = {
|
|
||||||
control: "button",
|
|
||||||
domainObject: primary.context.item,
|
|
||||||
method: function () {
|
|
||||||
let deleteFrameAction = tertiary.context.deleteFrame;
|
|
||||||
|
|
||||||
let prompt = openmct.overlays.dialog({
|
|
||||||
iconClass: 'alert',
|
|
||||||
message: `This action will remove this frame from this Flexible Layout. Do you want to continue?`,
|
|
||||||
buttons: [
|
|
||||||
{
|
|
||||||
label: 'Ok',
|
|
||||||
emphasis: 'true',
|
|
||||||
callback: function () {
|
|
||||||
deleteFrameAction(primary.context.frameId);
|
|
||||||
prompt.dismiss();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Cancel',
|
|
||||||
callback: function () {
|
|
||||||
prompt.dismiss();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
},
|
|
||||||
key: "remove",
|
|
||||||
icon: "icon-trash",
|
|
||||||
title: "Remove Frame"
|
|
||||||
};
|
|
||||||
toggleFrame = {
|
|
||||||
control: "toggle-button",
|
|
||||||
domainObject: secondary.context.item,
|
|
||||||
property: `configuration.containers[${containerIndex}].frames[${frameIndex}].noFrame`,
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
value: false,
|
|
||||||
icon: 'icon-frame-hide',
|
|
||||||
title: "Frame hidden"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: true,
|
|
||||||
icon: 'icon-frame-show',
|
|
||||||
title: "Frame visible"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
addContainer = {
|
|
||||||
control: "button",
|
|
||||||
domainObject: tertiary.context.item,
|
|
||||||
method: tertiary.context.addContainer,
|
|
||||||
key: "add",
|
|
||||||
icon: "icon-plus-in-rect",
|
|
||||||
title: 'Add Container'
|
|
||||||
};
|
|
||||||
|
|
||||||
} else if (primary.context.type === 'container') {
|
|
||||||
|
|
||||||
deleteContainer = {
|
|
||||||
control: "button",
|
|
||||||
domainObject: primary.context.item,
|
|
||||||
method: function () {
|
|
||||||
let removeContainer = secondary.context.deleteContainer,
|
|
||||||
containerId = primary.context.containerId;
|
|
||||||
|
|
||||||
let prompt = openmct.overlays.dialog({
|
|
||||||
iconClass: 'alert',
|
|
||||||
message: `This action will permanently delete container ${containerIndex + 1} from this Flexible Layout`,
|
|
||||||
buttons: [
|
|
||||||
{
|
|
||||||
label: 'Ok',
|
|
||||||
emphasis: 'true',
|
|
||||||
callback: function () {
|
|
||||||
removeContainer(containerId);
|
|
||||||
prompt.dismiss();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Cancel',
|
|
||||||
callback: function () {
|
|
||||||
prompt.dismiss();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
},
|
|
||||||
key: "remove",
|
|
||||||
icon: "icon-trash",
|
|
||||||
title: "Remove Container"
|
|
||||||
};
|
|
||||||
|
|
||||||
addContainer = {
|
|
||||||
control: "button",
|
|
||||||
domainObject: secondary.context.item,
|
|
||||||
method: secondary.context.addContainer,
|
|
||||||
key: "add",
|
|
||||||
icon: "icon-plus-in-rect",
|
|
||||||
title: 'Add Container'
|
|
||||||
};
|
|
||||||
|
|
||||||
} else if (primary.context.type === 'flexible-layout') {
|
|
||||||
|
|
||||||
addContainer = {
|
|
||||||
control: "button",
|
|
||||||
domainObject: primary.context.item,
|
|
||||||
method: primary.context.addContainer,
|
|
||||||
key: "add",
|
|
||||||
icon: "icon-plus-in-rect",
|
|
||||||
title: 'Add Container'
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
let toolbar = [
|
|
||||||
toggleContainer,
|
|
||||||
addContainer,
|
|
||||||
toggleFrame ? separator: undefined,
|
|
||||||
toggleFrame,
|
|
||||||
deleteFrame || deleteContainer ? separator: undefined,
|
|
||||||
deleteFrame,
|
|
||||||
deleteContainer
|
|
||||||
];
|
|
||||||
|
|
||||||
return toolbar.filter(button => button !== undefined);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ToolbarProvider;
|
|
||||||
@@ -1,10 +1,13 @@
|
|||||||
import uuid from 'uuid';
|
import Frame from './frame';
|
||||||
|
|
||||||
class Container {
|
class Container {
|
||||||
constructor (size) {
|
constructor (width) {
|
||||||
this.id = uuid();
|
this.frames = [new Frame({}, '', 'c-fl-frame--first-in-container')];
|
||||||
this.frames = [];
|
this.width = width;
|
||||||
this.size = size;
|
}
|
||||||
|
|
||||||
|
addFrame(frameObject) {
|
||||||
|
this.frames.push(frameObject);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
import uuid from 'uuid';
|
|
||||||
|
|
||||||
class Frame {
|
class Frame {
|
||||||
constructor(domainObjectIdentifier, size) {
|
constructor(domainObject, height, cssClass) {
|
||||||
this.id = uuid();
|
this.domainObject = domainObject;
|
||||||
this.domainObjectIdentifier = domainObjectIdentifier;
|
this.height = height;
|
||||||
this.size = size;
|
this.cssClass = cssClass ? cssClass : '';
|
||||||
|
|
||||||
this.noFrame = false;
|
this.noFrame = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ define([
|
|||||||
function FolderGridView(openmct) {
|
function FolderGridView(openmct) {
|
||||||
return {
|
return {
|
||||||
key: 'grid',
|
key: 'grid',
|
||||||
name: 'Grid View',
|
name: 'Grid Vue',
|
||||||
cssClass: 'icon-thumbs-strip',
|
cssClass: 'icon-thumbs-strip',
|
||||||
canView: function (domainObject) {
|
canView: function (domainObject) {
|
||||||
return domainObject.type === 'folder';
|
return domainObject.type === 'folder';
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ define([
|
|||||||
function FolderListView(openmct) {
|
function FolderListView(openmct) {
|
||||||
return {
|
return {
|
||||||
key: 'list-view',
|
key: 'list-view',
|
||||||
name: 'List View',
|
name: 'List Vue',
|
||||||
cssClass: 'icon-list-view',
|
cssClass: 'icon-list-view',
|
||||||
canView: function (domainObject) {
|
canView: function (domainObject) {
|
||||||
return domainObject.type === 'folder';
|
return domainObject.type === 'folder';
|
||||||
|
|||||||
@@ -57,7 +57,8 @@
|
|||||||
&__name {
|
&__name {
|
||||||
@include ellipsize();
|
@include ellipsize();
|
||||||
color: $colorItemFg;
|
color: $colorItemFg;
|
||||||
@include headerFont(1.2em);
|
font-size: 1.2em;
|
||||||
|
font-weight: 400;
|
||||||
margin-bottom: $interiorMarginSm;
|
margin-bottom: $interiorMarginSm;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,11 +150,11 @@
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import contextMenuGesture from '../../../ui/mixins/context-menu-gesture';
|
import contextMenu from '../../../ui/components/mixins/context-menu';
|
||||||
import objectLink from '../../../ui/mixins/object-link';
|
import objectLink from '../../../ui/components/mixins/object-link';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [contextMenuGesture, objectLink],
|
mixins: [contextMenu, objectLink],
|
||||||
props: ['item']
|
props: ['item']
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -4,8 +4,9 @@
|
|||||||
@click="navigate">
|
@click="navigate">
|
||||||
<td class="c-list-item__name">
|
<td class="c-list-item__name">
|
||||||
<a :href="objectLink" ref="objectLink">
|
<a :href="objectLink" ref="objectLink">
|
||||||
<div class="c-list-item__type-icon" :class="item.type.cssClass"></div>
|
<div class="c-list-item__type-icon"
|
||||||
<div class="c-list-item__name-value">{{item.model.name}}</div>
|
:class="item.type.cssClass"></div>
|
||||||
|
{{item.model.name}}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td class="c-list-item__type">{{ item.type.name }}</td>
|
<td class="c-list-item__type">{{ item.type.name }}</td>
|
||||||
@@ -19,24 +20,17 @@
|
|||||||
|
|
||||||
/******************************* LIST ITEM */
|
/******************************* LIST ITEM */
|
||||||
.c-list-item {
|
.c-list-item {
|
||||||
&__name a {
|
&__name {
|
||||||
display: flex;
|
@include ellipsize();
|
||||||
|
|
||||||
> * + * { margin-left: $interiorMarginSm; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__type-icon {
|
&__type-icon {
|
||||||
// Have to do it this way instead of using icon-* class, due to need to apply alias to the icon
|
|
||||||
color: $colorKey;
|
color: $colorKey;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 1em;
|
width: 1em;
|
||||||
margin-right:$interiorMarginSm;
|
margin-right:$interiorMarginSm;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__name-value {
|
|
||||||
@include ellipsize();
|
|
||||||
}
|
|
||||||
|
|
||||||
&.is-alias {
|
&.is-alias {
|
||||||
// Object is an alias to an original.
|
// Object is an alias to an original.
|
||||||
[class*='__type-icon'] {
|
[class*='__type-icon'] {
|
||||||
@@ -54,16 +48,17 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import contextMenuGesture from '../../../ui/mixins/context-menu-gesture';
|
import contextMenu from '../../../ui/components/mixins/context-menu';
|
||||||
import objectLink from '../../../ui/mixins/object-link';
|
import objectLink from '../../../ui/components/mixins/object-link';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [contextMenuGesture, objectLink],
|
mixins: [contextMenu, objectLink],
|
||||||
props: ['item'],
|
props: ['item'],
|
||||||
methods: {
|
methods: {
|
||||||
formatTime(timestamp, format) {
|
formatTime(timestamp, format) {
|
||||||
|
|||||||
@@ -42,8 +42,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<list-item v-for="item in sortedItems"
|
<list-item v-for="(item,index) in sortedItems"
|
||||||
:key="item.objectKeyString"
|
|
||||||
:item="item"
|
:item="item"
|
||||||
:object-path="item.objectPath">
|
:object-path="item.objectPath">
|
||||||
</list-item>
|
</list-item>
|
||||||
@@ -78,12 +77,9 @@
|
|||||||
|
|
||||||
td {
|
td {
|
||||||
$p: floor($interiorMargin * 1.5);
|
$p: floor($interiorMargin * 1.5);
|
||||||
@include ellipsize();
|
font-size: 1.1em;
|
||||||
line-height: 120%; // Needed for icon alignment
|
|
||||||
max-width: 0;
|
|
||||||
padding-top: $p;
|
padding-top: $p;
|
||||||
padding-bottom: $p;
|
padding-bottom: $p;
|
||||||
width: 25%;
|
|
||||||
|
|
||||||
&:not(.c-list-item__name) {
|
&:not(.c-list-item__name) {
|
||||||
color: $colorItemFgDetails;
|
color: $colorItemFgDetails;
|
||||||
@@ -103,20 +99,9 @@ export default {
|
|||||||
mixins: [compositionLoader],
|
mixins: [compositionLoader],
|
||||||
inject: ['domainObject', 'openmct'],
|
inject: ['domainObject', 'openmct'],
|
||||||
data() {
|
data() {
|
||||||
let sortBy = 'model.name',
|
|
||||||
ascending = true,
|
|
||||||
persistedSortOrder = window.localStorage.getItem('openmct-listview-sort-order');
|
|
||||||
|
|
||||||
if (persistedSortOrder) {
|
|
||||||
let parsed = JSON.parse(persistedSortOrder);
|
|
||||||
|
|
||||||
sortBy = parsed.sortBy;
|
|
||||||
ascending = parsed.ascending;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
sortBy,
|
sortBy: 'model.name',
|
||||||
ascending
|
ascending: true
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -136,17 +121,6 @@ export default {
|
|||||||
this.sortBy = field;
|
this.sortBy = field;
|
||||||
this.ascending = defaultDirection;
|
this.ascending = defaultDirection;
|
||||||
}
|
}
|
||||||
|
|
||||||
window.localStorage
|
|
||||||
.setItem(
|
|
||||||
'openmct-listview-sort-order',
|
|
||||||
JSON.stringify(
|
|
||||||
{
|
|
||||||
sortBy: this.sortBy,
|
|
||||||
ascending: this.ascending
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,8 +35,7 @@ export default {
|
|||||||
model: child,
|
model: child,
|
||||||
type: type.definition,
|
type: type.definition,
|
||||||
isAlias: this.domainObject.identifier.key !== child.location,
|
isAlias: this.domainObject.identifier.key !== child.location,
|
||||||
objectPath: [child].concat(this.openmct.router.path),
|
objectPath: [child].concat(openmct.router.path)
|
||||||
objectKeyString: this.openmct.objects.makeKeyString(child.identifier)
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
remove(identifier) {
|
remove(identifier) {
|
||||||
|
|||||||
@@ -21,9 +21,29 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define([
|
define([
|
||||||
"./src/controllers/NotebookController"
|
"./src/controllers/NotebookController",
|
||||||
|
"./src/controllers/NewEntryController",
|
||||||
|
"./src/controllers/SelectSnapshotController",
|
||||||
|
"./src/actions/NewEntryContextual",
|
||||||
|
"./src/actions/AnnotateSnapshot",
|
||||||
|
"./src/directives/MCTSnapshot",
|
||||||
|
"./src/directives/EntryDnd",
|
||||||
|
"./res/templates/controls/snapSelect.html",
|
||||||
|
"./res/templates/controls/embedControl.html",
|
||||||
|
"./res/templates/annotation.html",
|
||||||
|
"./res/templates/draggedEntry.html"
|
||||||
], function (
|
], function (
|
||||||
NotebookController
|
NotebookController,
|
||||||
|
NewEntryController,
|
||||||
|
SelectSnapshotController,
|
||||||
|
newEntryAction,
|
||||||
|
AnnotateSnapshotAction,
|
||||||
|
MCTSnapshotDirective,
|
||||||
|
EntryDndDirective,
|
||||||
|
snapSelectTemplate,
|
||||||
|
embedControlTemplate,
|
||||||
|
annotationTemplate,
|
||||||
|
draggedEntryTemplate
|
||||||
) {
|
) {
|
||||||
var installed = false;
|
var installed = false;
|
||||||
|
|
||||||
@@ -47,8 +67,9 @@ define([
|
|||||||
features: 'creation',
|
features: 'creation',
|
||||||
model: {
|
model: {
|
||||||
entries: [],
|
entries: [],
|
||||||
|
composition: [],
|
||||||
entryTypes: [],
|
entryTypes: [],
|
||||||
defaultSort: 'oldest'
|
defaultSort: '-createdOn'
|
||||||
},
|
},
|
||||||
properties: [
|
properties: [
|
||||||
{
|
{
|
||||||
@@ -58,17 +79,112 @@ define([
|
|||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
name: 'Newest First',
|
name: 'Newest First',
|
||||||
value: "newest"
|
value: "-createdOn",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Oldest First',
|
name: 'Oldest First',
|
||||||
value: "oldest"
|
value: "createdOn"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
cssClass: 'l-inline'
|
cssClass: 'l-inline'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
"key": "notebook-new-entry",
|
||||||
|
"implementation": newEntryAction,
|
||||||
|
"name": "New Notebook Entry",
|
||||||
|
"cssClass": "icon-notebook labeled",
|
||||||
|
"description": "Add a new Notebook entry",
|
||||||
|
"category": [
|
||||||
|
"view-control"
|
||||||
|
],
|
||||||
|
"depends": [
|
||||||
|
"$compile",
|
||||||
|
"$rootScope",
|
||||||
|
"dialogService",
|
||||||
|
"notificationService",
|
||||||
|
"linkService"
|
||||||
|
],
|
||||||
|
"priority": "preferred"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "annotate-snapshot",
|
||||||
|
"implementation": AnnotateSnapshotAction,
|
||||||
|
"name": "Annotate Snapshot",
|
||||||
|
"cssClass": "icon-pencil labeled",
|
||||||
|
"description": "Annotate embed's snapshot",
|
||||||
|
"category": "embed",
|
||||||
|
"depends": [
|
||||||
|
"dialogService",
|
||||||
|
"dndService",
|
||||||
|
"$rootScope"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
controllers: [
|
||||||
|
{
|
||||||
|
"key": "NewEntryController",
|
||||||
|
"implementation": NewEntryController,
|
||||||
|
"depends": ["$scope",
|
||||||
|
"$rootScope"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "selectSnapshotController",
|
||||||
|
"implementation": SelectSnapshotController,
|
||||||
|
"depends": ["$scope",
|
||||||
|
"$rootScope"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
controls: [
|
||||||
|
{
|
||||||
|
"key": "snapshot-select",
|
||||||
|
"template": snapSelectTemplate
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "embed-control",
|
||||||
|
"template": embedControlTemplate
|
||||||
|
}
|
||||||
|
],
|
||||||
|
templates: [
|
||||||
|
{
|
||||||
|
"key": "annotate-snapshot",
|
||||||
|
"template": annotationTemplate
|
||||||
|
}
|
||||||
|
],
|
||||||
|
directives: [
|
||||||
|
{
|
||||||
|
"key": "mctSnapshot",
|
||||||
|
"implementation": MCTSnapshotDirective,
|
||||||
|
"depends": [
|
||||||
|
"$rootScope",
|
||||||
|
"$document",
|
||||||
|
"exportImageService",
|
||||||
|
"dialogService",
|
||||||
|
"notificationService"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "mctEntryDnd",
|
||||||
|
"implementation": EntryDndDirective,
|
||||||
|
"depends": [
|
||||||
|
"$rootScope",
|
||||||
|
"$compile",
|
||||||
|
"dndService",
|
||||||
|
"typeService",
|
||||||
|
"notificationService"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
representations: [
|
||||||
|
{
|
||||||
|
"key": "draggedEntry",
|
||||||
|
"template": draggedEntryTemplate
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
2
src/plugins/notebook/res/templates/annotation.html
Normal file
2
src/plugins/notebook/res/templates/annotation.html
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<div class="snap-annotation" id="snap-annotation" ng-controller="ngModel.controller">
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
<!--
|
||||||
|
Open MCT, Copyright (c) 2009-2016, 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.
|
||||||
|
-->
|
||||||
|
<!--
|
||||||
|
This element appears in the overlay dialog when initiating a new Notebook Entry from a view's Notebook button -->
|
||||||
|
<div class='form-control'>
|
||||||
|
<ng-form name="mctControl">
|
||||||
|
<div class='fields' ng-controller="NewEntryController">
|
||||||
|
<div class="l-flex-row new-notebook-entry-embed l-entry-embed {{cssClass}}"
|
||||||
|
ng-class="{ 'has-snapshot' : snapToggle }">
|
||||||
|
<div class="holder flex-elem snap-thumb"
|
||||||
|
ng-if="snapToggle">
|
||||||
|
<img ng-src="{{snapshot.src}}" alt="{{snapshot.modified}}">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="holder flex-elem embed-info">
|
||||||
|
<div class="embed-title">{{objectName}}</div>
|
||||||
|
<div class="embed-date"
|
||||||
|
ng-if="snapToggle">{{snapshot.modified| date:'yyyy-MM-dd HH:mm:ss'}}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="holder flex-elem annotate-new"
|
||||||
|
ng-if="snapToggle">
|
||||||
|
<a class="s-button flex-elem icon-pencil "
|
||||||
|
title="Annotate this snapshot"
|
||||||
|
ng-click="annotateSnapshot()">
|
||||||
|
<span class="title-label">Annotate</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-form>
|
||||||
|
</div>
|
||||||
29
src/plugins/notebook/res/templates/controls/snapSelect.html
Normal file
29
src/plugins/notebook/res/templates/controls/snapSelect.html
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<!--
|
||||||
|
Open MCT, Copyright (c) 2014-2017, 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.
|
||||||
|
-->
|
||||||
|
<div class='form-control select' ng-controller="selectSnapshotController">
|
||||||
|
<select
|
||||||
|
ng-model="selectModel"
|
||||||
|
ng-options="opt.value as opt.name for opt in options"
|
||||||
|
ng-required="ngRequired"
|
||||||
|
name="mctControl">
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
38
src/plugins/notebook/res/templates/draggedEntry.html
Normal file
38
src/plugins/notebook/res/templates/draggedEntry.html
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<!--
|
||||||
|
Open MCT, Copyright (c) 2014-2017, 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.
|
||||||
|
-->
|
||||||
|
<div class="frame snap-frame frame-template t-frame-inner abs t-object-type-{{ representation.selected.key }}">
|
||||||
|
<div class="abs object-browse-bar l-flex-row">
|
||||||
|
<div class="btn-bar right l-flex-row flex-elem flex-justify-end flex-fixed">
|
||||||
|
<mct-representation
|
||||||
|
key="'switcher'"
|
||||||
|
ng-model="representation"
|
||||||
|
mct-object="domainObject">
|
||||||
|
</mct-representation>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="abs object-holder" data-entry = "{{parameters.entry}}" data-embed = "{{parameters.embed}}" mct-snapshot ng-if="representation.selected.key">
|
||||||
|
<mct-representation
|
||||||
|
key="representation.selected.key"
|
||||||
|
mct-object="representation.selected.key && domainObject">
|
||||||
|
</mct-representation>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<li class="c-notebook__entry c-ne has-local-controls"
|
<li class="c-notebook__entry c-ne has-local-controls"
|
||||||
v-on:drop="dropOnEntry(entry.id, $event)"
|
v-on:drop="dropOnEntry(entry.id)"
|
||||||
v-on:dragover="dragoverOnEntry"
|
v-on:dragover="dragoverOnEntry"
|
||||||
>
|
>
|
||||||
<div class="c-ne__time-and-content">
|
<div class="c-ne__time-and-content">
|
||||||
@@ -19,16 +19,15 @@
|
|||||||
<div class="c-ne__embeds">
|
<div class="c-ne__embeds">
|
||||||
<notebook-embed
|
<notebook-embed
|
||||||
v-for="(embed, index) in entry.embeds"
|
v-for="(embed, index) in entry.embeds"
|
||||||
:key="index"
|
v-bind:embed="embed"
|
||||||
:embed="embed"
|
v-bind:entry="entry"
|
||||||
:entry="entry"
|
|
||||||
></notebook-embed>
|
></notebook-embed>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="c-ne__local-controls--hidden">
|
<div class="c-ne__local-controls--hidden">
|
||||||
<button class="c-click-icon c-click-icon--major icon-trash"
|
<button class="c-click-icon icon-trash"
|
||||||
title="Delete this entry"
|
title="Delete this entry"
|
||||||
v-on:click="deleteEntry"></button>
|
v-on:click="deleteEntry"></button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,36 +1,37 @@
|
|||||||
<div class="c-notebook">
|
<div class="c-notebook">
|
||||||
<div class="c-notebook__head">
|
<div class="c-notebook__head">
|
||||||
<search class="c-notebook__search"
|
<search class="c-notebook__search"
|
||||||
:value="entrySearch"
|
v-model="entrySearch"
|
||||||
@input="search"
|
v-on:input="search($event)"
|
||||||
@clear="search">
|
v-on:clear="entrySearch = ''; search($event)"></search>
|
||||||
</search>
|
<div class="c-notebook__controls">
|
||||||
<div class="c-notebook__controls ">
|
<div class="select c-notebook__controls__time">
|
||||||
<select class="c-notebook__controls__time" v-model="showTime">
|
<select v-model="showTime">
|
||||||
<option value="0" selected="selected">Show all</option>
|
<option value="0" selected="selected">Show all</option>
|
||||||
<option value="1">Last hour</option>
|
<option value="1">Last hour</option>
|
||||||
<option value="8">Last 8 hours</option>
|
<option value="8">Last 8 hours</option>
|
||||||
<option value="24">Last 24 hours</option>
|
<option value="24">Last 24 hours</option>
|
||||||
</select>
|
</select>
|
||||||
<select class="c-notebook__controls__time" v-model="sortEntries">
|
</div>
|
||||||
<option value="newest" :selected="sortEntries === 'newest'">Newest first</option>
|
<div class="select c-notebook__controls__sort">
|
||||||
<option value="oldest" :selected="sortEntries === 'oldest'">Oldest first</option>
|
<select v-model="sortEntries">
|
||||||
</select>
|
<option value="-createdOn" selected="selected">Newest first</option>
|
||||||
|
<option value="createdOn">Oldest first</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="c-notebook__drag-area icon-plus"
|
<div class="c-notebook__drag-area icon-plus"
|
||||||
@click="newEntry($event)"
|
v-on:click="newEntry($event)"
|
||||||
@drop="newEntry($event)"
|
id="newEntry" mct-entry-dnd>
|
||||||
id="newEntry">
|
|
||||||
<span class="c-notebook__drag-area__label">To start a new entry, click here or drag and drop any object</span>
|
<span class="c-notebook__drag-area__label">To start a new entry, click here or drag and drop any object</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="c-notebook__entries" ng-mouseover="handleActive()">
|
<div class="c-notebook__entries" ng-mouseover="handleActive()">
|
||||||
<ul>
|
<ul>
|
||||||
<notebook-entry
|
<notebook-entry
|
||||||
v-for="(entry,index) in filteredAndSortedEntries"
|
v-for="entry in filterBySearch(entries, entrySearch)"
|
||||||
:key="entry.key"
|
v-bind:entry="entry"
|
||||||
:entry="entry">
|
></notebook-entry>
|
||||||
</notebook-entry>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
162
src/plugins/notebook/src/actions/AnnotateSnapshot.js
Normal file
162
src/plugins/notebook/src/actions/AnnotateSnapshot.js
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2017, 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.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module defining viewSnapshot (Originally NewWindowAction). Created by vwoeltje on 11/18/14.
|
||||||
|
*/
|
||||||
|
define(
|
||||||
|
["painterro", "zepto"],
|
||||||
|
function (Painterro, $) {
|
||||||
|
|
||||||
|
var annotationStruct = {
|
||||||
|
title: "Annotate Snapshot",
|
||||||
|
template: "annotate-snapshot",
|
||||||
|
options: [{
|
||||||
|
name: "OK",
|
||||||
|
key: "ok",
|
||||||
|
description: "save annotation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Cancel",
|
||||||
|
key: "cancel",
|
||||||
|
description: "cancel editing"
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
|
||||||
|
function AnnotateSnapshot(dialogService, dndService, $rootScope, context) {
|
||||||
|
context = context || {};
|
||||||
|
|
||||||
|
// Choose the object to be opened into a new tab
|
||||||
|
this.domainObject = context.selectedObject || context.domainObject;
|
||||||
|
this.dialogService = dialogService;
|
||||||
|
this.dndService = dndService;
|
||||||
|
this.$rootScope = $rootScope;
|
||||||
|
}
|
||||||
|
|
||||||
|
AnnotateSnapshot.prototype.perform = function ($event, snapshot, embedId, entryId) {
|
||||||
|
|
||||||
|
var DOMAIN_OBJECT = this.domainObject;
|
||||||
|
var ROOTSCOPE = this.$rootScope;
|
||||||
|
var painterro;
|
||||||
|
var save = false;
|
||||||
|
|
||||||
|
var controller = ['$scope', '$timeout', function PainterroController($scope, $timeout) {
|
||||||
|
$(document.body).find('.l-dialog .outer-holder').addClass('annotation-dialog');
|
||||||
|
|
||||||
|
// Timeout is necessary because Painterro uses document.getElementById, and mct-include
|
||||||
|
// hasn't added the dialog to the DOM yet.
|
||||||
|
$timeout(function () {
|
||||||
|
painterro = Painterro({
|
||||||
|
id: 'snap-annotation',
|
||||||
|
activeColor: '#ff0000',
|
||||||
|
activeColorAlpha: 1.0,
|
||||||
|
activeFillColor: '#fff',
|
||||||
|
activeFillColorAlpha: 0.0,
|
||||||
|
backgroundFillColor: '#000',
|
||||||
|
backgroundFillColorAlpha: 0.0,
|
||||||
|
defaultFontSize: 16,
|
||||||
|
defaultLineWidth: 2,
|
||||||
|
defaultTool: 'ellipse',
|
||||||
|
hiddenTools: ['save', 'open', 'close', 'eraser', 'pixelize', 'rotate', 'settings', 'resize'],
|
||||||
|
translation: {
|
||||||
|
name: 'en',
|
||||||
|
strings: {
|
||||||
|
lineColor: 'Line',
|
||||||
|
fillColor: 'Fill',
|
||||||
|
lineWidth: 'Size',
|
||||||
|
textColor: 'Color',
|
||||||
|
fontSize: 'Size',
|
||||||
|
fontStyle: 'Style'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
saveHandler: function (image, done) {
|
||||||
|
if (save) {
|
||||||
|
if (entryId && embedId) {
|
||||||
|
var elementPos = DOMAIN_OBJECT.model.entries.map(function (x) {
|
||||||
|
return x.createdOn;
|
||||||
|
}).indexOf(entryId);
|
||||||
|
var entryEmbeds = DOMAIN_OBJECT.model.entries[elementPos].embeds;
|
||||||
|
var embedPos = entryEmbeds.map(function (x) {
|
||||||
|
return x.id;
|
||||||
|
}).indexOf(embedId);
|
||||||
|
|
||||||
|
saveSnap(image.asBlob(), embedPos, elementPos, DOMAIN_OBJECT);
|
||||||
|
}else {
|
||||||
|
ROOTSCOPE.snapshot = {'src': image.asDataURL('image/png'),
|
||||||
|
'modified': Date.now()};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
done(true);
|
||||||
|
}
|
||||||
|
}).show(snapshot);
|
||||||
|
});
|
||||||
|
}];
|
||||||
|
|
||||||
|
annotationStruct.model = {'controller': controller};
|
||||||
|
|
||||||
|
function saveNotes(param) {
|
||||||
|
if (param === 'ok') {
|
||||||
|
save = true;
|
||||||
|
}else {
|
||||||
|
save = false;
|
||||||
|
ROOTSCOPE.snapshot = "annotationCancelled";
|
||||||
|
}
|
||||||
|
painterro.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
function rejectNotes() {
|
||||||
|
save = false;
|
||||||
|
ROOTSCOPE.snapshot = "annotationCancelled";
|
||||||
|
painterro.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveSnap(url, embedPos, entryPos, domainObject) {
|
||||||
|
var snap = false;
|
||||||
|
|
||||||
|
if (embedPos !== -1 && entryPos !== -1) {
|
||||||
|
var reader = new window.FileReader();
|
||||||
|
reader.readAsDataURL(url);
|
||||||
|
reader.onloadend = function () {
|
||||||
|
snap = reader.result;
|
||||||
|
domainObject.useCapability('mutation', function (model) {
|
||||||
|
if (model.entries[entryPos]) {
|
||||||
|
model.entries[entryPos].embeds[embedPos].snapshot = {
|
||||||
|
'src': snap,
|
||||||
|
'type': url.type,
|
||||||
|
'size': url.size,
|
||||||
|
'modified': Date.now()
|
||||||
|
};
|
||||||
|
model.entries[entryPos].embeds[embedPos].id = Date.now();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dialogService.getUserChoice(annotationStruct)
|
||||||
|
.then(saveNotes, rejectNotes);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
return AnnotateSnapshot;
|
||||||
|
}
|
||||||
|
);
|
||||||
204
src/plugins/notebook/src/actions/NewEntryContextual.js
Normal file
204
src/plugins/notebook/src/actions/NewEntryContextual.js
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2017, 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 () {
|
||||||
|
|
||||||
|
var SNAPSHOT_TEMPLATE = '<mct-representation key="\'draggedEntry\'"' +
|
||||||
|
'class="t-rep-frame holder"' +
|
||||||
|
'mct-object="selObj">' +
|
||||||
|
'</mct-representation>';
|
||||||
|
|
||||||
|
var NEW_TASK_FORM = {
|
||||||
|
name: "Create a Notebook Entry",
|
||||||
|
hint: "Please select one Notebook",
|
||||||
|
sections: [{
|
||||||
|
rows: [{
|
||||||
|
name: 'Entry',
|
||||||
|
key: 'entry',
|
||||||
|
control: 'textarea',
|
||||||
|
required: false,
|
||||||
|
"cssClass": "l-textarea-sm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Embed Type',
|
||||||
|
key: 'withSnapshot',
|
||||||
|
control: 'snapshot-select',
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"name": "Link and Snapshot",
|
||||||
|
"value": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Link only",
|
||||||
|
"value": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Embed',
|
||||||
|
key: 'embedObject',
|
||||||
|
control: 'embed-control'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Save in Notebook',
|
||||||
|
key: 'saveNotebook',
|
||||||
|
control: 'locator',
|
||||||
|
validate: validateLocation
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
|
||||||
|
function NewEntryContextual($compile, $rootScope, dialogService, notificationService, linkService, context) {
|
||||||
|
context = context || {};
|
||||||
|
this.domainObject = context.selectedObject || context.domainObject;
|
||||||
|
this.dialogService = dialogService;
|
||||||
|
this.notificationService = notificationService;
|
||||||
|
this.linkService = linkService;
|
||||||
|
this.$rootScope = $rootScope;
|
||||||
|
this.$compile = $compile;
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateLocation(newParentObj) {
|
||||||
|
return newParentObj.model.type === 'notebook';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
NewEntryContextual.prototype.perform = function () {
|
||||||
|
var self = this;
|
||||||
|
var domainObj = this.domainObject;
|
||||||
|
var notification = this.notificationService;
|
||||||
|
var dialogService = this.dialogService;
|
||||||
|
var rootScope = this.$rootScope;
|
||||||
|
rootScope.newEntryText = '';
|
||||||
|
// // Create the overlay element and add it to the document's body
|
||||||
|
this.$rootScope.selObj = domainObj;
|
||||||
|
this.$rootScope.selValue = "";
|
||||||
|
var newScope = rootScope.$new();
|
||||||
|
newScope.selObj = domainObj;
|
||||||
|
newScope.selValue = "";
|
||||||
|
this.$compile(SNAPSHOT_TEMPLATE)(newScope);
|
||||||
|
|
||||||
|
this.$rootScope.$watch("snapshot", setSnapshot);
|
||||||
|
|
||||||
|
function setSnapshot(value) {
|
||||||
|
if (value === "annotationCancelled") {
|
||||||
|
rootScope.snapshot = rootScope.lastValue;
|
||||||
|
rootScope.lastValue = '';
|
||||||
|
|
||||||
|
} else if (value && value !== rootScope.lastValue) {
|
||||||
|
var overlayModel = {
|
||||||
|
title: NEW_TASK_FORM.name,
|
||||||
|
message: NEW_TASK_FORM.message,
|
||||||
|
structure: NEW_TASK_FORM,
|
||||||
|
value: {'entry': rootScope.newEntryText || ""}
|
||||||
|
};
|
||||||
|
|
||||||
|
rootScope.currentDialog = overlayModel;
|
||||||
|
|
||||||
|
dialogService.getDialogResponse(
|
||||||
|
"overlay-dialog",
|
||||||
|
overlayModel,
|
||||||
|
function () {
|
||||||
|
return overlayModel.value;
|
||||||
|
}
|
||||||
|
).then(addNewEntry);
|
||||||
|
|
||||||
|
rootScope.lastValue = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addNewEntry(options) {
|
||||||
|
options.selectedModel = options.embedObject.getModel();
|
||||||
|
options.cssClass = options.embedObject.getCapability('type').typeDef.cssClass;
|
||||||
|
if (self.$rootScope.snapshot) {
|
||||||
|
options.snapshot = self.$rootScope.snapshot;
|
||||||
|
self.$rootScope.snapshot = undefined;
|
||||||
|
}else {
|
||||||
|
options.snapshot = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!options.withSnapshot) {
|
||||||
|
options.snapshot = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
createSnap(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createSnap(options) {
|
||||||
|
options.saveNotebook.useCapability('mutation', function (model) {
|
||||||
|
var entries = model.entries;
|
||||||
|
var lastEntry = entries[entries.length - 1];
|
||||||
|
var date = Date.now();
|
||||||
|
|
||||||
|
if (lastEntry === undefined || lastEntry.text || lastEntry.embeds) {
|
||||||
|
model.entries.push({
|
||||||
|
'id': date,
|
||||||
|
'createdOn': date,
|
||||||
|
'text': options.entry,
|
||||||
|
'embeds': [{'type': options.embedObject.getId(),
|
||||||
|
'id': '' + date,
|
||||||
|
'cssClass': options.cssClass,
|
||||||
|
'name': options.selectedModel.name,
|
||||||
|
'snapshot': options.snapshot
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
}else {
|
||||||
|
model.entries[entries.length - 1] = {
|
||||||
|
'id': date,
|
||||||
|
'createdOn': date,
|
||||||
|
'text': options.entry,
|
||||||
|
'embeds': [{'type': options.embedObject.getId(),
|
||||||
|
'id': '' + date,
|
||||||
|
'cssClass': options.cssClass,
|
||||||
|
'name': options.selectedModel.name,
|
||||||
|
'snapshot': options.snapshot
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
notification.info({
|
||||||
|
title: "Notebook Entry created"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
NewEntryContextual.appliesTo = function (context) {
|
||||||
|
var domainObject = context.domainObject;
|
||||||
|
|
||||||
|
if (domainObject) {
|
||||||
|
if (domainObject.getModel().type === 'notebook') {
|
||||||
|
// do not allow in context of a notebook
|
||||||
|
return false;
|
||||||
|
} else if (domainObject.getModel().type.includes('imagery')) {
|
||||||
|
// do not allow in the context of an object with imagery
|
||||||
|
// (because of cross domain issue with snapshot)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
return NewEntryContextual;
|
||||||
|
}
|
||||||
|
);
|
||||||
130
src/plugins/notebook/src/actions/snapshotAction.js
Normal file
130
src/plugins/notebook/src/actions/snapshotAction.js
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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(
|
||||||
|
['zepto'],
|
||||||
|
function ($) {
|
||||||
|
|
||||||
|
function SnapshotAction (exportImageService, dialogService, context) {
|
||||||
|
this.exportImageService = exportImageService;
|
||||||
|
this.dialogService = dialogService;
|
||||||
|
this.domainObject = context.domainObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
SnapshotAction.prototype.perform = function () {
|
||||||
|
var elementToSnapshot =
|
||||||
|
$(document.body).find(".overlay .object-holder")[0] ||
|
||||||
|
$(document.body).find("[key='representation.selected.key']")[0];
|
||||||
|
|
||||||
|
$(elementToSnapshot).addClass("s-status-taking-snapshot");
|
||||||
|
|
||||||
|
this.exportImageService.exportPNGtoSRC(elementToSnapshot).then(function (blob) {
|
||||||
|
$(elementToSnapshot).removeClass("s-status-taking-snapshot");
|
||||||
|
|
||||||
|
if (blob) {
|
||||||
|
var reader = new window.FileReader();
|
||||||
|
reader.readAsDataURL(blob);
|
||||||
|
reader.onloadend = function () {
|
||||||
|
this.saveSnapshot(reader.result, blob.type, blob.size);
|
||||||
|
}.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}.bind(this));
|
||||||
|
};
|
||||||
|
|
||||||
|
SnapshotAction.prototype.saveSnapshot = function (imageURL, imageType, imageSize) {
|
||||||
|
var taskForm = this.generateTaskForm(),
|
||||||
|
domainObject = this.domainObject,
|
||||||
|
domainObjectId = domainObject.getId(),
|
||||||
|
cssClass = domainObject.getCapability('type').typeDef.cssClass,
|
||||||
|
name = domainObject.model.name;
|
||||||
|
|
||||||
|
this.dialogService.getDialogResponse(
|
||||||
|
'overlay-dialog',
|
||||||
|
taskForm,
|
||||||
|
function () {
|
||||||
|
return taskForm.value;
|
||||||
|
}
|
||||||
|
).then(function (options) {
|
||||||
|
var snapshotObject = {
|
||||||
|
src: imageURL,
|
||||||
|
type: imageType,
|
||||||
|
size: imageSize
|
||||||
|
};
|
||||||
|
|
||||||
|
options.notebook.useCapability('mutation', function (model) {
|
||||||
|
var date = Date.now();
|
||||||
|
|
||||||
|
model.entries.push({
|
||||||
|
id: 'entry-' + date,
|
||||||
|
createdOn: date,
|
||||||
|
text: options.entry,
|
||||||
|
embeds: [{
|
||||||
|
name: name,
|
||||||
|
cssClass: cssClass,
|
||||||
|
type: domainObjectId,
|
||||||
|
id: 'embed-' + date,
|
||||||
|
createdOn: date,
|
||||||
|
snapshot: snapshotObject
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
SnapshotAction.prototype.generateTaskForm = function () {
|
||||||
|
var taskForm = {
|
||||||
|
name: "Create a Notebook Entry",
|
||||||
|
hint: "Please select a Notebook",
|
||||||
|
sections: [{
|
||||||
|
rows: [{
|
||||||
|
name: 'Entry',
|
||||||
|
key: 'entry',
|
||||||
|
control: 'textarea',
|
||||||
|
required: false,
|
||||||
|
"cssClass": "l-textarea-sm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Save in Notebook',
|
||||||
|
key: 'notebook',
|
||||||
|
control: 'locator',
|
||||||
|
validate: validateLocation
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
|
||||||
|
var overlayModel = {
|
||||||
|
title: taskForm.name,
|
||||||
|
message: 'AHAHAH',
|
||||||
|
structure: taskForm,
|
||||||
|
value: {'entry': ""}
|
||||||
|
};
|
||||||
|
|
||||||
|
function validateLocation(newParentObj) {
|
||||||
|
return newParentObj.model.type === 'notebook';
|
||||||
|
}
|
||||||
|
|
||||||
|
return overlayModel;
|
||||||
|
};
|
||||||
|
|
||||||
|
return SnapshotAction;
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -275,6 +275,7 @@ function (
|
|||||||
|
|
||||||
function menuClickHandler(e) {
|
function menuClickHandler(e) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
window.setTimeout(dismiss, 300);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dismiss any menu which was already showing
|
// Dismiss any menu which was already showing
|
||||||
|
|||||||
@@ -105,27 +105,23 @@ function (
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
EntryController.prototype.dropOnEntry = function (entryid, event) {
|
EntryController.prototype.dropOnEntry = function (entryId) {
|
||||||
|
var selectedObject = this.dndService.getData('mct-domain-object'),
|
||||||
|
selectedObjectId = selectedObject.getId(),
|
||||||
|
selectedModel = selectedObject.getModel(),
|
||||||
|
cssClass = selectedObject.getCapability('type').typeDef.cssClass,
|
||||||
|
entryPos = this.entryPosById(entryId),
|
||||||
|
currentEntryEmbeds = this.domainObject.entries[entryPos].embeds,
|
||||||
|
newEmbed = {
|
||||||
|
type: selectedObjectId,
|
||||||
|
id: '' + Date.now(),
|
||||||
|
cssClass: cssClass,
|
||||||
|
name: selectedModel.name,
|
||||||
|
snapshot: ''
|
||||||
|
};
|
||||||
|
|
||||||
var data = event.dataTransfer.getData('openmct/domain-object-path');
|
currentEntryEmbeds.push(newEmbed);
|
||||||
|
this.openmct.objects.mutate(this.domainObject, 'entries[' + entryPos + '].embeds', currentEntryEmbeds);
|
||||||
if (data) {
|
|
||||||
var selectedObject = JSON.parse(data)[0],
|
|
||||||
selectedObjectId = selectedObject.identifier.key,
|
|
||||||
cssClass = this.openmct.types.get(selectedObject.type),
|
|
||||||
entryPos = this.entryPosById(entryid),
|
|
||||||
currentEntryEmbeds = this.domainObject.entries[entryPos].embeds,
|
|
||||||
newEmbed = {
|
|
||||||
type: selectedObjectId,
|
|
||||||
id: '' + Date.now(),
|
|
||||||
cssClass: cssClass,
|
|
||||||
name: selectedObject.name,
|
|
||||||
snapshot: ''
|
|
||||||
};
|
|
||||||
|
|
||||||
currentEntryEmbeds.push(newEmbed);
|
|
||||||
this.openmct.objects.mutate(this.domainObject, 'entries[' + entryPos + '].embeds', currentEntryEmbeds);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
EntryController.prototype.dragoverOnEntry = function () {
|
EntryController.prototype.dragoverOnEntry = function () {
|
||||||
@@ -136,6 +132,7 @@ function (
|
|||||||
return {
|
return {
|
||||||
openmct: this.openmct,
|
openmct: this.openmct,
|
||||||
domainObject: this.domainObject,
|
domainObject: this.domainObject,
|
||||||
|
dndService: this.dndService,
|
||||||
dialogService: this.dialogService,
|
dialogService: this.dialogService,
|
||||||
currentEntryValue: this.currentEntryValue
|
currentEntryValue: this.currentEntryValue
|
||||||
};
|
};
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user