mirror of
https://github.com/nasa/openmct.git
synced 2025-06-29 04:03:03 +00:00
Compare commits
15 Commits
vue-conduc
...
context-me
Author | SHA1 | Date | |
---|---|---|---|
485e948abe | |||
64b9d4c24a | |||
88bcb6078e | |||
5f9f3cd8e8 | |||
814b404614 | |||
ba2bb2180b | |||
72cdb352f0 | |||
cedf942c0c | |||
27506a3757 | |||
acc4e03c88 | |||
9a6090cd02 | |||
f40c9fa6f9 | |||
e7cdb334de | |||
afca6cd2e9 | |||
3c324cbea0 |
@ -26,12 +26,16 @@ define([
|
||||
"./src/NotificationLaunchController",
|
||||
"./src/DialogLaunchIndicator",
|
||||
"./src/NotificationLaunchIndicator",
|
||||
"./res/dialog-launch.html",
|
||||
"./res/notification-launch.html",
|
||||
'legacyRegistry'
|
||||
], function (
|
||||
DialogLaunchController,
|
||||
NotificationLaunchController,
|
||||
DialogLaunchIndicator,
|
||||
NotificationLaunchIndicator,
|
||||
DialogLaunch,
|
||||
NotificationLaunch,
|
||||
legacyRegistry
|
||||
) {
|
||||
"use strict";
|
||||
@ -41,11 +45,11 @@ define([
|
||||
"templates": [
|
||||
{
|
||||
"key": "dialogLaunchTemplate",
|
||||
"templateUrl": "dialog-launch.html"
|
||||
"template": DialogLaunch
|
||||
},
|
||||
{
|
||||
"key": "notificationLaunchTemplate",
|
||||
"templateUrl": "notification-launch.html"
|
||||
"template": NotificationLaunch
|
||||
}
|
||||
],
|
||||
"controllers": [
|
||||
|
@ -51,76 +51,26 @@ define(
|
||||
return actionTexts[Math.floor(Math.random()*3)];
|
||||
}
|
||||
|
||||
function getExampleActions() {
|
||||
var actions = [
|
||||
{
|
||||
label: "Try Again",
|
||||
callback: function () {
|
||||
$log.debug("Try Again pressed");
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "Remove",
|
||||
callback: function () {
|
||||
$log.debug("Remove pressed");
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "Cancel",
|
||||
callback: function () {
|
||||
$log.debug("Cancel pressed");
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
// Randomly remove some actions off the top; leave at least one
|
||||
actions.splice(0,Math.floor(Math.random() * actions.length));
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
function getExampleSeverity() {
|
||||
var severities = [
|
||||
"info",
|
||||
"alert",
|
||||
"error"
|
||||
];
|
||||
return severities[Math.floor(Math.random() * severities.length)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch a new notification with a severity level of 'Error'.
|
||||
*/
|
||||
$scope.newError = function(){
|
||||
|
||||
$scope.newError = function () {
|
||||
notificationService.notify({
|
||||
title: "Example error notification " + messageCounter++,
|
||||
hint: "An error has occurred",
|
||||
severity: "error",
|
||||
primaryOption: {
|
||||
label: 'Retry',
|
||||
callback: function() {
|
||||
$log.info('Retry clicked');
|
||||
}
|
||||
},
|
||||
options: getExampleActions()});
|
||||
severity: "error"
|
||||
});
|
||||
};
|
||||
/**
|
||||
* Launch a new notification with a severity of 'Alert'.
|
||||
*/
|
||||
$scope.newAlert = function(){
|
||||
|
||||
$scope.newAlert = function () {
|
||||
notificationService.notify({
|
||||
title: "Alert notification " + (messageCounter++),
|
||||
hint: "This is an alert message",
|
||||
severity: "alert",
|
||||
primaryOption: {
|
||||
label: 'Retry',
|
||||
callback: function() {
|
||||
$log.info('Retry clicked');
|
||||
}
|
||||
},
|
||||
options: getExampleActions()});
|
||||
autoDismiss: true
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@ -128,39 +78,38 @@ define(
|
||||
* Launch a new notification with a progress bar that is updated
|
||||
* periodically, tracking an ongoing process.
|
||||
*/
|
||||
$scope.newProgress = function(){
|
||||
|
||||
$scope.newProgress = function () {
|
||||
var notificationModel = {
|
||||
title: "Progress notification example",
|
||||
severity: "info",
|
||||
progress: 0,
|
||||
actionText: getExampleActionText(),
|
||||
unknownProgress: false
|
||||
actionText: getExampleActionText()
|
||||
};
|
||||
|
||||
/**
|
||||
* Simulate an ongoing process and update the progress bar.
|
||||
* @param notification
|
||||
*/
|
||||
function incrementProgress(notificationModel) {
|
||||
function incrementProgress() {
|
||||
notificationModel.progress = Math.min(100, Math.floor(notificationModel.progress + Math.random() * 30));
|
||||
notificationModel.progressText = ["Estimated time" +
|
||||
" remaining:" +
|
||||
" about ", 60 - Math.floor((notificationModel.progress / 100) * 60), " seconds"].join(" ");
|
||||
if (notificationModel.progress < 100) {
|
||||
$timeout(function(){incrementProgress(notificationModel);}, 1000);
|
||||
$timeout(function () {
|
||||
incrementProgress(notificationModel);
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
notificationService.notify(notificationModel);
|
||||
incrementProgress(notificationModel);
|
||||
incrementProgress();
|
||||
};
|
||||
|
||||
/**
|
||||
* Launch a new notification with severity level of INFO.
|
||||
*/
|
||||
$scope.newInfo = function(){
|
||||
|
||||
$scope.newInfo = function () {
|
||||
notificationService.info({
|
||||
title: "Example Info notification " + messageCounter++
|
||||
});
|
||||
|
@ -58,7 +58,7 @@ define([], function () {
|
||||
|
||||
function checkNavigation() {
|
||||
var navigatedObject = navigationService.getNavigation();
|
||||
if (navigatedObject.hasCapability('context')) {
|
||||
if (navigatedObject && navigatedObject.hasCapability('context')) {
|
||||
if (!navigatedObject.getCapability('editor').isEditContextRoot()) {
|
||||
preventOrphanNavigation(navigatedObject);
|
||||
}
|
||||
|
@ -405,7 +405,8 @@ define([
|
||||
"description": "Provides transactional editing capabilities",
|
||||
"implementation": EditorCapability,
|
||||
"depends": [
|
||||
"transactionService"
|
||||
"transactionService",
|
||||
"openmct"
|
||||
]
|
||||
}
|
||||
],
|
||||
|
@ -36,9 +36,11 @@ define(
|
||||
*/
|
||||
function EditorCapability(
|
||||
transactionService,
|
||||
openmct,
|
||||
domainObject
|
||||
) {
|
||||
this.transactionService = transactionService;
|
||||
this.openmct = openmct;
|
||||
this.domainObject = domainObject;
|
||||
}
|
||||
|
||||
@ -48,27 +50,19 @@ define(
|
||||
* or finish() are called.
|
||||
*/
|
||||
EditorCapability.prototype.edit = function () {
|
||||
this.transactionService.startTransaction();
|
||||
console.warn('DEPRECATED: cannot edit via edit capability, use openmct.editor instead.');
|
||||
this.openmct.editor.edit();
|
||||
this.domainObject.getCapability('status').set('editing', true);
|
||||
};
|
||||
|
||||
function isEditContextRoot(domainObject) {
|
||||
return domainObject.getCapability('status').get('editing');
|
||||
}
|
||||
|
||||
function isEditing(domainObject) {
|
||||
return isEditContextRoot(domainObject) ||
|
||||
domainObject.hasCapability('context') &&
|
||||
isEditing(domainObject.getCapability('context').getParent());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether this object, or any of its ancestors are
|
||||
* currently being edited.
|
||||
* @returns boolean
|
||||
*/
|
||||
EditorCapability.prototype.inEditContext = function () {
|
||||
return isEditing(this.domainObject);
|
||||
console.warn('DEPRECATION WARNING: isEditing checks must be done via openmct.editor.');
|
||||
return this.openmct.editor.isEditing();
|
||||
};
|
||||
|
||||
/**
|
||||
@ -77,7 +71,8 @@ define(
|
||||
* @returns {*}
|
||||
*/
|
||||
EditorCapability.prototype.isEditContextRoot = function () {
|
||||
return isEditContextRoot(this.domainObject);
|
||||
console.warn('DEPRECATION WARNING: isEditing checks must be done via openmct.editor.');
|
||||
return this.openmct.editor.isEditing();
|
||||
};
|
||||
|
||||
/**
|
||||
@ -86,10 +81,7 @@ define(
|
||||
* @returns {*}
|
||||
*/
|
||||
EditorCapability.prototype.save = function () {
|
||||
var transactionService = this.transactionService;
|
||||
return transactionService.commit().then(function () {
|
||||
transactionService.startTransaction();
|
||||
});
|
||||
console.warn('DEPRECATED: cannot save via edit capability, use openmct.editor instead.');
|
||||
};
|
||||
|
||||
EditorCapability.prototype.invoke = EditorCapability.prototype.edit;
|
||||
@ -100,16 +92,7 @@ define(
|
||||
* @returns {*}
|
||||
*/
|
||||
EditorCapability.prototype.finish = function () {
|
||||
var domainObject = this.domainObject;
|
||||
|
||||
if (this.transactionService.isActive()) {
|
||||
return this.transactionService.cancel().then(function () {
|
||||
domainObject.getCapability("status").set("editing", false);
|
||||
return domainObject;
|
||||
});
|
||||
} else {
|
||||
return Promise.resolve(domainObject);
|
||||
}
|
||||
console.warn('DEPRECATED: cannot finish via edit capability, use openmct.editor instead.');
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -60,7 +60,7 @@ define(
|
||||
};
|
||||
//If the notification is dismissed by the user, close
|
||||
// the dialog.
|
||||
notification.onDismiss(function () {
|
||||
notification.on('dismiss', function () {
|
||||
dialog.dismiss();
|
||||
});
|
||||
|
||||
|
@ -23,33 +23,17 @@
|
||||
define([
|
||||
"./src/NotificationIndicatorController",
|
||||
"./src/NotificationIndicator",
|
||||
"./src/NotificationService",
|
||||
"./res/notification-indicator.html",
|
||||
'legacyRegistry'
|
||||
], function (
|
||||
NotificationIndicatorController,
|
||||
NotificationIndicator,
|
||||
NotificationService,
|
||||
notificationIndicatorTemplate,
|
||||
legacyRegistry
|
||||
) {
|
||||
|
||||
legacyRegistry.register("platform/commonUI/notification", {
|
||||
"extensions": {
|
||||
"constants": [
|
||||
{
|
||||
"key": "DEFAULT_AUTO_DISMISS",
|
||||
"value": 3000
|
||||
},
|
||||
{
|
||||
"key": "FORCE_AUTO_DISMISS",
|
||||
"value": 1000
|
||||
},
|
||||
{
|
||||
"key": "MINIMIZE_TIMEOUT",
|
||||
"value": 300
|
||||
}
|
||||
],
|
||||
"templates": [
|
||||
{
|
||||
"key": "notificationIndicatorTemplate",
|
||||
@ -76,12 +60,11 @@ define([
|
||||
"services": [
|
||||
{
|
||||
"key": "notificationService",
|
||||
"implementation": NotificationService,
|
||||
"implementation": function (openmct) {
|
||||
return openmct.notifications;
|
||||
},
|
||||
"depends": [
|
||||
"$timeout",
|
||||
"topic",
|
||||
"DEFAULT_AUTO_DISMISS",
|
||||
"MINIMIZE_TIMEOUT"
|
||||
"openmct"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
@ -1,437 +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.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* This bundle implements the notification service, which can be used to
|
||||
* show banner notifications to the user. Banner notifications
|
||||
* are used to inform users of events in a non-intrusive way. As
|
||||
* much as possible, notifications share a model with blocking
|
||||
* dialogs so that the same information can be provided in a dialog
|
||||
* and then minimized to a banner notification if needed.
|
||||
*
|
||||
* @namespace platform/commonUI/notification
|
||||
*/
|
||||
define(
|
||||
['moment'],
|
||||
function (moment) {
|
||||
|
||||
/**
|
||||
* A representation of a user action. Options are provided to
|
||||
* dialogs and notifications and are shown as buttons.
|
||||
*
|
||||
* @typedef {object} NotificationOption
|
||||
* @property {string} label the label to appear on the button for
|
||||
* this action
|
||||
* @property {function} callback a callback function to be invoked
|
||||
* when the button is clicked
|
||||
*/
|
||||
|
||||
/**
|
||||
* A representation of a banner notification. Banner notifications
|
||||
* are used to inform users of events in a non-intrusive way. As
|
||||
* much as possible, notifications share a model with blocking
|
||||
* dialogs so that the same information can be provided in a dialog
|
||||
* and then minimized to a banner notification if needed, or vice-versa.
|
||||
*
|
||||
* @typedef {object} NotificationModel
|
||||
* @property {string} title The title of the message
|
||||
* @property {string} severity The importance of the message (one of
|
||||
* 'info', 'alert', or 'error' where info < alert <error)
|
||||
* @property {number} [progress] The completion status of a task
|
||||
* represented numerically
|
||||
* @property {boolean} [unknownProgress] a boolean indicating that the
|
||||
* progress of the underlying task is unknown. This will result in a
|
||||
* visually distinct progress bar.
|
||||
* @property {boolean} [autoDismiss] If truthy, dialog will
|
||||
* be automatically minimized or dismissed (depending on severity).
|
||||
* Additionally, if the provided value is a number, it will be used
|
||||
* as the delay period before being dismissed.
|
||||
* @property {boolean} [dismissable=true] If true, notification will
|
||||
* include an option to dismiss it completely.
|
||||
* @property {NotificationOption} [primaryOption] the default user
|
||||
* response to
|
||||
* this message. Will be represented as a button with the provided
|
||||
* label and action. May be used by banner notifications to display
|
||||
* only the most important option to users.
|
||||
* @property {NotificationOption[]} [options] any additional
|
||||
* actions the user can take. Will be represented as additional buttons
|
||||
* that may or may not be available from a banner.
|
||||
* @see DialogModel
|
||||
*/
|
||||
|
||||
/**
|
||||
* A wrapper object that is returned as a handle to a newly created
|
||||
* notification. Wraps the notifications model and decorates with
|
||||
* functions to dismiss or minimize the notification.
|
||||
*
|
||||
* @typedef {object} Notification
|
||||
* @property {function} dismiss This method is added to the object
|
||||
* returned by {@link NotificationService#notify} and can be used to
|
||||
* dismiss this notification. Dismissing a notification will remove
|
||||
* it completely and it will not appear in the notification indicator
|
||||
* @property {function} minimize This method is added to the object
|
||||
* returned by {@link NotificationService#notify} and can be used to
|
||||
* minimize this notification. Minimizing a notification will send
|
||||
* it to the notification indicator
|
||||
* @property {function} dismissOrMinimize This method is added to the
|
||||
* object returned by {@link NotificationService#notify}. It will
|
||||
* hide the notification by either dismissing or minimizing it,
|
||||
* depending on severity. Typically this is the method that should
|
||||
* be used for dismissing a notification. If more control is
|
||||
* required, then the minimize or dismiss functions can be called
|
||||
* individually.
|
||||
* @property {function} onDismiss Allows listening for on dismiss
|
||||
* events. This allows cleanup etc. when the notification is dismissed.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The notification service is responsible for informing the user of
|
||||
* events via the use of banner notifications.
|
||||
* @memberof platform/commonUI/notification
|
||||
* @constructor
|
||||
* @param $timeout the Angular $timeout service
|
||||
* @param defaultAutoDismissTimeout The period of time that an
|
||||
* auto-dismissed message will be displayed for.
|
||||
* @param minimizeAnimationTimeout When notifications are minimized, a brief
|
||||
* animation is shown. This animation requires some time to execute,
|
||||
* so a timeout is required before the notification is hidden
|
||||
*/
|
||||
function NotificationService($timeout, topic, defaultAutoDismissTimeout, minimizeAnimationTimeout) {
|
||||
this.notifications = [];
|
||||
this.$timeout = $timeout;
|
||||
this.highest = { severity: "info" };
|
||||
this.AUTO_DISMISS_TIMEOUT = defaultAutoDismissTimeout;
|
||||
this.MINIMIZE_ANIMATION_TIMEOUT = minimizeAnimationTimeout;
|
||||
this.topic = topic;
|
||||
|
||||
/*
|
||||
* A context in which to hold the active notification and a
|
||||
* handle to its timeout.
|
||||
*/
|
||||
this.active = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Minimize a notification. The notification will still be available
|
||||
* from the notification list. Typically notifications with a
|
||||
* severity of 'info' should not be minimized, but rather
|
||||
* dismissed. If you're not sure which is appropriate,
|
||||
* use {@link Notification#dismissOrMinimize}
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
NotificationService.prototype.minimize = function (service, notification) {
|
||||
//Check this is a known notification
|
||||
var index = service.notifications.indexOf(notification);
|
||||
|
||||
if (service.active.timeout) {
|
||||
/*
|
||||
Method can be called manually (clicking dismiss) or
|
||||
automatically from an auto-timeout. this.active.timeout
|
||||
acts as a semaphore to prevent race conditions. Cancel any
|
||||
timeout in progress (for the case where a manual dismiss
|
||||
has shortcut an active auto-dismiss), and clear the
|
||||
semaphore.
|
||||
*/
|
||||
service.$timeout.cancel(service.active.timeout);
|
||||
delete service.active.timeout;
|
||||
}
|
||||
|
||||
if (index >= 0) {
|
||||
notification.model.minimized = true;
|
||||
//Add a brief timeout before showing the next notification
|
||||
// in order to allow the minimize animation to run through.
|
||||
service.$timeout(function () {
|
||||
service.setActiveNotification(service.selectNextNotification());
|
||||
}, service.MINIMIZE_ANIMATION_TIMEOUT);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Completely removes a notification. This will dismiss it from the
|
||||
* message banner and remove it from the list of notifications.
|
||||
* Typically only notifications with a severity of info should be
|
||||
* dismissed. If you're not sure whether to dismiss or minimize a
|
||||
* notification, use {@link Notification#dismissOrMinimize}.
|
||||
* dismiss
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
NotificationService.prototype.dismiss = function (service, notification) {
|
||||
//Check this is a known notification
|
||||
var index = service.notifications.indexOf(notification);
|
||||
|
||||
if (service.active.timeout) {
|
||||
/* Method can be called manually (clicking dismiss) or
|
||||
* automatically from an auto-timeout. this.active.timeout
|
||||
* acts as a semaphore to prevent race conditions. Cancel any
|
||||
* timeout in progress (for the case where a manual dismiss
|
||||
* has shortcut an active auto-dismiss), and clear the
|
||||
* semaphore.
|
||||
*/
|
||||
|
||||
service.$timeout.cancel(service.active.timeout);
|
||||
delete service.active.timeout;
|
||||
}
|
||||
|
||||
if (index >= 0) {
|
||||
service.notifications.splice(index, 1);
|
||||
}
|
||||
service.setActiveNotification(service.selectNextNotification());
|
||||
|
||||
this.setHighestSeverity();
|
||||
};
|
||||
|
||||
/**
|
||||
* Depending on the severity of the notification will selectively
|
||||
* dismiss or minimize where appropriate.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
NotificationService.prototype.dismissOrMinimize = function (notification) {
|
||||
var model = notification.model;
|
||||
if (model.severity === "info") {
|
||||
if (model.autoDismiss === false) {
|
||||
notification.minimize();
|
||||
} else {
|
||||
notification.dismiss();
|
||||
}
|
||||
} else {
|
||||
notification.minimize();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the notification that is currently visible in the banner area
|
||||
* @returns {Notification}
|
||||
*/
|
||||
NotificationService.prototype.getActiveNotification = function () {
|
||||
return this.active.notification;
|
||||
};
|
||||
|
||||
/**
|
||||
* A convenience method for info notifications. Notifications
|
||||
* created via this method will be auto-dismissed after a default
|
||||
* wait period unless explicitly forbidden by the caller through
|
||||
* the {autoDismiss} property on the {NotificationModel}, in which
|
||||
* case the notification will be minimized after the wait.
|
||||
* @param {NotificationModel | string} message either a string for
|
||||
* the title of the notification message, or a {@link NotificationModel}
|
||||
* defining the options notification to display
|
||||
* @returns {Notification} the provided notification decorated with
|
||||
* functions to dismiss or minimize
|
||||
*/
|
||||
NotificationService.prototype.info = function (message) {
|
||||
var notificationModel = typeof message === "string" ? {title: message} : message;
|
||||
notificationModel.severity = "info";
|
||||
return this.notify(notificationModel);
|
||||
};
|
||||
|
||||
/**
|
||||
* A convenience method for alert notifications. Notifications
|
||||
* created via this method will will have severity of "alert" enforced
|
||||
* @param {NotificationModel | string} message either a string for
|
||||
* the title of the alert message with default options, or a
|
||||
* {@link NotificationModel} defining the options notification to
|
||||
* display
|
||||
* @returns {Notification} the provided notification decorated with
|
||||
* functions to dismiss or minimize
|
||||
*/
|
||||
NotificationService.prototype.alert = function (message) {
|
||||
var notificationModel = typeof message === "string" ? {title: message} : message;
|
||||
notificationModel.severity = "alert";
|
||||
return this.notify(notificationModel);
|
||||
};
|
||||
|
||||
/**
|
||||
* A convenience method for error notifications. Notifications
|
||||
* created via this method will will have severity of "error" enforced
|
||||
* @param {NotificationModel | string} message either a string for
|
||||
* the title of the error message with default options, or a
|
||||
* {@link NotificationModel} defining the options notification to
|
||||
* display
|
||||
* @returns {Notification} the provided notification decorated with
|
||||
* functions to dismiss or minimize
|
||||
*/
|
||||
NotificationService.prototype.error = function (message) {
|
||||
var notificationModel = typeof message === "string" ? {title: message} : message;
|
||||
notificationModel.severity = "error";
|
||||
return this.notify(notificationModel);
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
NotificationService.prototype.setHighestSeverity = function () {
|
||||
var severity = {
|
||||
"info": 1,
|
||||
"alert": 2,
|
||||
"error": 3
|
||||
};
|
||||
this.highest.severity = this.notifications.reduce(function (previous, notification) {
|
||||
if (severity[notification.model.severity] > severity[previous]) {
|
||||
return notification.model.severity;
|
||||
} else {
|
||||
return previous;
|
||||
}
|
||||
}, "info");
|
||||
};
|
||||
|
||||
/**
|
||||
* Notifies the user of an event. If there is a banner notification
|
||||
* already active, then it will be dismissed or minimized automatically,
|
||||
* and the provided notification displayed in its place.
|
||||
*
|
||||
* @param {NotificationModel} notificationModel The notification to
|
||||
* display
|
||||
* @returns {Notification} the provided notification decorated with
|
||||
* functions to {@link Notification#dismiss} or {@link Notification#minimize}
|
||||
*/
|
||||
NotificationService.prototype.notify = function (notificationModel) {
|
||||
var self = this,
|
||||
notification,
|
||||
activeNotification = self.active.notification,
|
||||
topic = this.topic();
|
||||
|
||||
notificationModel.severity = notificationModel.severity || "info";
|
||||
notificationModel.timestamp = moment.utc().format('YYYY-MM-DD hh:mm:ss.ms');
|
||||
|
||||
notification = {
|
||||
model: notificationModel,
|
||||
|
||||
minimize: function () {
|
||||
self.minimize(self, notification);
|
||||
},
|
||||
|
||||
dismiss: function () {
|
||||
self.dismiss(self, notification);
|
||||
topic.notify();
|
||||
},
|
||||
|
||||
dismissOrMinimize: function () {
|
||||
self.dismissOrMinimize(notification);
|
||||
},
|
||||
|
||||
onDismiss: function (callback) {
|
||||
topic.listen(callback);
|
||||
}
|
||||
};
|
||||
|
||||
//Notifications support a 'dismissable' attribute. This is a
|
||||
// convenience to support adding a 'dismiss' option to the
|
||||
// notification for the common case of dismissing a
|
||||
// notification. Could also be done manually by specifying an
|
||||
// option on the model
|
||||
if (notificationModel.dismissable !== false) {
|
||||
notificationModel.options = notificationModel.options || [];
|
||||
notificationModel.options.unshift({
|
||||
label: "Dismiss",
|
||||
callback: function () {
|
||||
notification.dismiss();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.notifications.push(notification);
|
||||
|
||||
this.setHighestSeverity();
|
||||
|
||||
/*
|
||||
Check if there is already an active (ie. visible) notification
|
||||
*/
|
||||
if (!this.active.notification) {
|
||||
this.setActiveNotification(notification);
|
||||
|
||||
} else if (!this.active.timeout) {
|
||||
/*
|
||||
If there is already an active notification, time it out. If it's
|
||||
already got a timeout in progress (either because it has had
|
||||
timeout forced because of a queue of messages, or it had an
|
||||
autodismiss specified), leave it to run. Otherwise force a
|
||||
timeout.
|
||||
|
||||
This notification has been added to queue and will be
|
||||
serviced as soon as possible.
|
||||
*/
|
||||
this.active.timeout = this.$timeout(function () {
|
||||
activeNotification.dismissOrMinimize();
|
||||
}, this.AUTO_DISMISS_TIMEOUT);
|
||||
}
|
||||
|
||||
return notification;
|
||||
};
|
||||
|
||||
/**
|
||||
* Used internally by the NotificationService
|
||||
* @private
|
||||
*/
|
||||
NotificationService.prototype.setActiveNotification = function (notification) {
|
||||
var shouldAutoDismiss;
|
||||
this.active.notification = notification;
|
||||
|
||||
if (!notification) {
|
||||
delete this.active.timeout;
|
||||
return;
|
||||
}
|
||||
|
||||
if (notification.model.severity === "info") {
|
||||
shouldAutoDismiss = true;
|
||||
} else {
|
||||
shouldAutoDismiss = notification.model.autoDismiss;
|
||||
}
|
||||
|
||||
if (shouldAutoDismiss || this.selectNextNotification()) {
|
||||
this.active.timeout = this.$timeout(function () {
|
||||
notification.dismissOrMinimize();
|
||||
}, this.AUTO_DISMISS_TIMEOUT);
|
||||
} else {
|
||||
delete this.active.timeout;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Used internally by the NotificationService
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
NotificationService.prototype.selectNextNotification = function () {
|
||||
var notification,
|
||||
i = 0;
|
||||
|
||||
/*
|
||||
Loop through the notifications queue and find the first one that
|
||||
has not already been minimized (manually or otherwise).
|
||||
*/
|
||||
for (; i < this.notifications.length; i++) {
|
||||
notification = this.notifications[i];
|
||||
|
||||
if (!notification.model.minimized &&
|
||||
notification !== this.active.notification) {
|
||||
|
||||
return notification;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return NotificationService;
|
||||
}
|
||||
);
|
@ -1,275 +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.
|
||||
*****************************************************************************/
|
||||
/*global describe,it,expect,beforeEach,jasmine*/
|
||||
|
||||
define(
|
||||
['../src/NotificationService'],
|
||||
function (NotificationService) {
|
||||
|
||||
describe("The notification service ", function () {
|
||||
var notificationService,
|
||||
mockTimeout,
|
||||
mockAutoDismiss,
|
||||
mockMinimizeTimeout,
|
||||
mockTopicFunction,
|
||||
mockTopicObject,
|
||||
infoModel,
|
||||
alertModel,
|
||||
errorModel;
|
||||
|
||||
function elapseTimeout() {
|
||||
mockTimeout.calls.mostRecent().args[0]();
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
mockTimeout = jasmine.createSpy("$timeout");
|
||||
mockTopicFunction = jasmine.createSpy("topic");
|
||||
mockTopicObject = jasmine.createSpyObj("topicObject", ["listen", "notify"]);
|
||||
mockTopicFunction.and.returnValue(mockTopicObject);
|
||||
|
||||
mockAutoDismiss = mockMinimizeTimeout = 1000;
|
||||
notificationService = new NotificationService(mockTimeout, mockTopicFunction, mockAutoDismiss, mockMinimizeTimeout);
|
||||
|
||||
infoModel = {
|
||||
title: "Mock Info Notification",
|
||||
severity: "info"
|
||||
};
|
||||
|
||||
alertModel = {
|
||||
title: "Mock Alert Notification",
|
||||
severity: "alert"
|
||||
};
|
||||
|
||||
errorModel = {
|
||||
title: "Mock Error Notification",
|
||||
severity: "error"
|
||||
};
|
||||
});
|
||||
|
||||
it("notifies listeners on dismissal of notification", function () {
|
||||
var dismissListener = jasmine.createSpy("ondismiss");
|
||||
var notification = notificationService.notify(infoModel);
|
||||
notification.onDismiss(dismissListener);
|
||||
expect(mockTopicObject.listen).toHaveBeenCalled();
|
||||
notification.dismiss();
|
||||
expect(mockTopicObject.notify).toHaveBeenCalled();
|
||||
mockTopicObject.listen.calls.mostRecent().args[0]();
|
||||
expect(dismissListener).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("dismisses a notification when the notification's dismiss method is used", function () {
|
||||
var notification = notificationService.info(infoModel);
|
||||
notification.dismiss();
|
||||
expect(notificationService.getActiveNotification()).toBeUndefined();
|
||||
expect(notificationService.notifications.length).toEqual(0);
|
||||
});
|
||||
|
||||
it("minimizes a notification when the notification's minimize method is used", function () {
|
||||
var notification = notificationService.info(infoModel);
|
||||
notification.minimize();
|
||||
elapseTimeout(); // needed for the minimize animation timeout
|
||||
expect(notificationService.getActiveNotification()).toBeUndefined();
|
||||
expect(notificationService.notifications.length).toEqual(1);
|
||||
expect(notificationService.notifications[0]).toEqual(notification);
|
||||
});
|
||||
|
||||
describe("when receiving info notifications", function () {
|
||||
it("minimizes info notifications if the caller disables auto-dismiss", function () {
|
||||
infoModel.autoDismiss = false;
|
||||
var notification = notificationService.info(infoModel);
|
||||
elapseTimeout();
|
||||
// 2nd elapse for the minimize animation timeout
|
||||
elapseTimeout();
|
||||
expect(notificationService.getActiveNotification()).toBeUndefined();
|
||||
expect(notificationService.notifications.length).toEqual(1);
|
||||
expect(notificationService.notifications[0]).toEqual(notification);
|
||||
});
|
||||
|
||||
it("dismisses info notifications if the caller ignores auto-dismiss", function () {
|
||||
notificationService.info(infoModel);
|
||||
elapseTimeout();
|
||||
expect(notificationService.getActiveNotification()).toBeUndefined();
|
||||
expect(notificationService.notifications.length).toEqual(0);
|
||||
});
|
||||
|
||||
it("dismisses info notifications if the caller requests auto-dismiss", function () {
|
||||
infoModel.autoDismiss = true;
|
||||
notificationService.info(infoModel);
|
||||
elapseTimeout();
|
||||
expect(notificationService.getActiveNotification()).toBeUndefined();
|
||||
expect(notificationService.notifications.length).toEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("when receiving alert notifications", function () {
|
||||
it("minimizes alert notifications if the caller enables auto-dismiss", function () {
|
||||
alertModel.autoDismiss = true;
|
||||
var notification = notificationService.alert(alertModel);
|
||||
elapseTimeout();
|
||||
elapseTimeout();
|
||||
expect(notificationService.getActiveNotification()).toBeUndefined();
|
||||
expect(notificationService.notifications.length).toEqual(1);
|
||||
expect(notificationService.notifications[0]).toEqual(notification);
|
||||
});
|
||||
|
||||
it("keeps alert notifications active if the caller disables auto-dismiss", function () {
|
||||
mockTimeout.and.callFake(function (callback, time) {
|
||||
callback();
|
||||
});
|
||||
alertModel.autoDismiss = false;
|
||||
var notification = notificationService.alert(alertModel);
|
||||
expect(notificationService.getActiveNotification()).toEqual(notification);
|
||||
expect(notificationService.notifications.length).toEqual(1);
|
||||
expect(notificationService.notifications[0]).toEqual(notification);
|
||||
});
|
||||
|
||||
it("keeps alert notifications active if the caller ignores auto-dismiss", function () {
|
||||
mockTimeout.and.callFake(function (callback, time) {
|
||||
callback();
|
||||
});
|
||||
var notification = notificationService.alert(alertModel);
|
||||
expect(notificationService.getActiveNotification()).toEqual(notification);
|
||||
expect(notificationService.notifications.length).toEqual(1);
|
||||
expect(notificationService.notifications[0]).toEqual(notification);
|
||||
});
|
||||
});
|
||||
|
||||
describe("when receiving error notifications", function () {
|
||||
it("minimizes error notifications if the caller enables auto-dismiss", function () {
|
||||
errorModel.autoDismiss = true;
|
||||
var notification = notificationService.error(errorModel);
|
||||
elapseTimeout();
|
||||
elapseTimeout();
|
||||
expect(notificationService.getActiveNotification()).toBeUndefined();
|
||||
expect(notificationService.notifications.length).toEqual(1);
|
||||
expect(notificationService.notifications[0]).toEqual(notification);
|
||||
});
|
||||
|
||||
it("keeps error notifications active if the caller disables auto-dismiss", function () {
|
||||
mockTimeout.and.callFake(function (callback, time) {
|
||||
callback();
|
||||
});
|
||||
errorModel.autoDismiss = false;
|
||||
var notification = notificationService.error(errorModel);
|
||||
expect(notificationService.getActiveNotification()).toEqual(notification);
|
||||
expect(notificationService.notifications.length).toEqual(1);
|
||||
expect(notificationService.notifications[0]).toEqual(notification);
|
||||
});
|
||||
|
||||
it("keeps error notifications active if the caller ignores auto-dismiss", function () {
|
||||
mockTimeout.and.callFake(function (callback, time) {
|
||||
callback();
|
||||
});
|
||||
var notification = notificationService.error(errorModel);
|
||||
expect(notificationService.getActiveNotification()).toEqual(notification);
|
||||
expect(notificationService.notifications.length).toEqual(1);
|
||||
expect(notificationService.notifications[0]).toEqual(notification);
|
||||
});
|
||||
});
|
||||
|
||||
describe("when called with multiple notifications", function () {
|
||||
it("auto-dismisses the previously active notification, making the new notification active", function () {
|
||||
var activeNotification;
|
||||
infoModel.autoDismiss = false;
|
||||
//First pre-load with a info message
|
||||
notificationService.notify(infoModel);
|
||||
activeNotification = notificationService.getActiveNotification();
|
||||
//Initially expect the active notification to be info
|
||||
expect(activeNotification.model).toBe(infoModel);
|
||||
//Then notify of an error
|
||||
notificationService.notify(errorModel);
|
||||
//But it should be auto-dismissed and replaced with the
|
||||
// error notification
|
||||
elapseTimeout();
|
||||
//Two timeouts, one is to force minimization after
|
||||
// displaying the message for a minimum period, the
|
||||
// second is to allow minimization animation to take place.
|
||||
elapseTimeout();
|
||||
activeNotification = notificationService.getActiveNotification();
|
||||
expect(activeNotification.model).toBe(errorModel);
|
||||
});
|
||||
|
||||
it("auto-minimizes an active error notification", function () {
|
||||
var activeNotification;
|
||||
//First pre-load with an error message
|
||||
notificationService.notify(errorModel);
|
||||
//Then notify of info
|
||||
notificationService.notify(infoModel);
|
||||
expect(notificationService.notifications.length).toEqual(2);
|
||||
//Mock the auto-minimize
|
||||
elapseTimeout();
|
||||
//Two timeouts, one is to force minimization after
|
||||
// displaying the message for a minimum period, the
|
||||
// second is to allow minimization animation to take place.
|
||||
elapseTimeout();
|
||||
//Previous error message should be minimized, not
|
||||
// dismissed
|
||||
expect(notificationService.notifications.length).toEqual(2);
|
||||
activeNotification = notificationService.getActiveNotification();
|
||||
expect(activeNotification.model).toBe(infoModel);
|
||||
expect(errorModel.minimized).toEqual(true);
|
||||
});
|
||||
|
||||
it("auto-minimizes errors when a number of them arrive in short succession", function () {
|
||||
var activeNotification,
|
||||
error2 = {
|
||||
title: "Second Mock Error Notification",
|
||||
severity: "error"
|
||||
},
|
||||
error3 = {
|
||||
title: "Third Mock Error Notification",
|
||||
severity: "error"
|
||||
};
|
||||
|
||||
//First pre-load with a info message
|
||||
notificationService.notify(errorModel);
|
||||
//Then notify of a third error
|
||||
notificationService.notify(error2);
|
||||
notificationService.notify(error3);
|
||||
expect(notificationService.notifications.length).toEqual(3);
|
||||
//Mock the auto-minimize
|
||||
elapseTimeout();
|
||||
//Two timeouts, one is to force minimization after
|
||||
// displaying the message for a minimum period, the
|
||||
// second is to allow minimization animation to take place.
|
||||
elapseTimeout();
|
||||
//Previous error message should be minimized, not
|
||||
// dismissed
|
||||
expect(notificationService.notifications.length).toEqual(3);
|
||||
activeNotification = notificationService.getActiveNotification();
|
||||
expect(activeNotification.model).toBe(error2);
|
||||
expect(errorModel.minimized).toEqual(true);
|
||||
|
||||
//Mock the second auto-minimize
|
||||
elapseTimeout();
|
||||
//Two timeouts, one is to force minimization after
|
||||
// displaying the message for a minimum period, the
|
||||
// second is to allow minimization animation to take place.
|
||||
elapseTimeout();
|
||||
activeNotification = notificationService.getActiveNotification();
|
||||
expect(activeNotification.model).toBe(error3);
|
||||
expect(error2.minimized).toEqual(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
@ -19,10 +19,10 @@
|
||||
this source code distribution or the Licensing information page available
|
||||
at runtime from the About dialog for additional information.
|
||||
-->
|
||||
<a class="l-hyperlink s-hyperlink" ng-controller="HyperlinkController as hyperlink" href="{{domainObject.getModel().url}}"
|
||||
<a class="c-hyperlink u-links" ng-controller="HyperlinkController as hyperlink" href="{{domainObject.getModel().url}}"
|
||||
ng-attr-target="{{hyperlink.openNewTab() ? '_blank' : undefined}}"
|
||||
ng-class="{
|
||||
's-button': hyperlink.isButton()
|
||||
}">
|
||||
<span class="label">{{domainObject.getModel().displayText}}</span>
|
||||
'c-hyperlink--button u-fills-container' : hyperlink.isButton(),
|
||||
'c-hyperlink--link' : !hyperlink.isButton() }">
|
||||
<span class="c-hyperlink__label">{{domainObject.getModel().displayText}}</span>
|
||||
</a>
|
||||
|
@ -1,83 +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.
|
||||
-->
|
||||
|
||||
<div class="abs l-layout {{ domainObject.getModel().layoutAdvancedCss }}"
|
||||
ng-controller="LayoutController as controller"
|
||||
ng-click="controller.bypassSelection($event)">
|
||||
|
||||
<!-- Background grid -->
|
||||
<div class="l-grid-holder"
|
||||
ng-show="!controller.drilledIn"
|
||||
ng-click="controller.bypassSelection($event)">
|
||||
<div class="l-grid l-grid-x"
|
||||
ng-if="!controller.getGridSize()[0] < 3"
|
||||
ng-style="{ 'background-size': controller.getGridSize() [0] + 'px 100%' }"></div>
|
||||
<div class="l-grid l-grid-y"
|
||||
ng-if="!controller.getGridSize()[1] < 3"
|
||||
ng-style="{ 'background-size': '100% ' + controller.getGridSize() [1] + 'px' }"></div>
|
||||
</div>
|
||||
|
||||
<div class="frame t-frame-outer child-frame panel s-selectable s-moveable s-hover-border t-object-type-{{ childObject.getModel().type }}"
|
||||
data-layout-id="{{childObject.getId() + '-' + $id}}"
|
||||
ng-class="{ 'no-frame': !controller.hasFrame(childObject), 's-drilled-in': controller.isDrilledIn(childObject) }"
|
||||
ng-repeat="childObject in composition"
|
||||
ng-init="controller.selectIfNew(childObject.getId() + '-' + $id, childObject)"
|
||||
mct-selectable="controller.getContext(childObject)"
|
||||
ng-dblclick="controller.drill($event, childObject)"
|
||||
ng-style="controller.getFrameStyle(childObject.getId())">
|
||||
|
||||
<mct-representation key="'frame'"
|
||||
class="t-rep-frame holder contents abs"
|
||||
mct-object="childObject">
|
||||
</mct-representation>
|
||||
<!-- Drag handles -->
|
||||
<span class="abs t-edit-handle-holder" ng-if="controller.selected(childObject) && !controller.isDrilledIn(childObject)">
|
||||
<span class="edit-handle edit-move"
|
||||
mct-drag-down="controller.startDrag(childObject.getId(), [1,1], [0,0])"
|
||||
mct-drag="controller.continueDrag(delta)"
|
||||
mct-drag-up="controller.endDrag()">
|
||||
</span>
|
||||
|
||||
<span class="edit-corner edit-resize-nw"
|
||||
mct-drag-down="controller.startDrag(childObject.getId(), [1,1], [-1,-1])"
|
||||
mct-drag="controller.continueDrag(delta)"
|
||||
mct-drag-up="controller.endDrag()">
|
||||
</span>
|
||||
<span class="edit-corner edit-resize-ne"
|
||||
mct-drag-down="controller.startDrag(childObject.getId(), [0,1], [1,-1])"
|
||||
mct-drag="controller.continueDrag(delta)"
|
||||
mct-drag-up="controller.endDrag()">
|
||||
</span>
|
||||
<span class="edit-corner edit-resize-sw"
|
||||
mct-drag-down="controller.startDrag(childObject.getId(), [1,0], [-1,1])"
|
||||
mct-drag="controller.continueDrag(delta)"
|
||||
mct-drag-up="controller.endDrag()">
|
||||
</span>
|
||||
<span class="edit-corner edit-resize-se"
|
||||
mct-drag-down="controller.startDrag(childObject.getId(), [0,0], [1,1])"
|
||||
mct-drag="controller.continueDrag(delta)"
|
||||
mct-drag-up="controller.endDrag()">
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
</div>
|
@ -1,524 +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.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* This bundle implements object types and associated views for
|
||||
* display-building.
|
||||
* @namespace platform/features/layout
|
||||
*/
|
||||
define(
|
||||
[
|
||||
'zepto',
|
||||
'./LayoutDrag'
|
||||
],
|
||||
function (
|
||||
$,
|
||||
LayoutDrag
|
||||
) {
|
||||
|
||||
var DEFAULT_DIMENSIONS = [12, 8],
|
||||
DEFAULT_GRID_SIZE = [32, 32],
|
||||
MINIMUM_FRAME_SIZE = [320, 180];
|
||||
|
||||
var DEFAULT_HIDDEN_FRAME_TYPES = [
|
||||
'hyperlink'
|
||||
];
|
||||
|
||||
/**
|
||||
* The LayoutController is responsible for supporting the
|
||||
* Layout view. It arranges frames according to saved configuration
|
||||
* and provides methods for updating these based on mouse
|
||||
* movement.
|
||||
* @memberof platform/features/layout
|
||||
* @constructor
|
||||
* @param {Scope} $scope the controller's Angular scope
|
||||
*/
|
||||
function LayoutController($scope, $element, openmct) {
|
||||
var self = this,
|
||||
callbackCount = 0;
|
||||
|
||||
this.$element = $element;
|
||||
|
||||
// Update grid size when it changed
|
||||
function updateGridSize(layoutGrid) {
|
||||
var oldSize = self.gridSize;
|
||||
|
||||
self.gridSize = layoutGrid || DEFAULT_GRID_SIZE;
|
||||
|
||||
// Only update panel positions if this actually changed things
|
||||
if (self.gridSize[0] !== oldSize[0] ||
|
||||
self.gridSize[1] !== oldSize[1]) {
|
||||
self.layoutPanels(Object.keys(self.positions));
|
||||
}
|
||||
}
|
||||
|
||||
// Position a panel after a drop event
|
||||
function handleDrop(e, id, position) {
|
||||
if (e.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.configuration = $scope.configuration || {};
|
||||
$scope.configuration.panels = $scope.configuration.panels || {};
|
||||
|
||||
self.openmct.objects.get(id).then(function (object) {
|
||||
$scope.configuration.panels[id] = {
|
||||
position: [
|
||||
Math.floor(position.x / self.gridSize[0]),
|
||||
Math.floor(position.y / self.gridSize[1])
|
||||
],
|
||||
dimensions: self.defaultDimensions(),
|
||||
hasFrame: self.getDefaultFrame(object.type)
|
||||
};
|
||||
|
||||
// Store the id so that the newly-dropped object
|
||||
// gets selected during refresh composition
|
||||
self.droppedIdToSelectAfterRefresh = id;
|
||||
|
||||
self.commit();
|
||||
|
||||
// Populate template-facing position for this id
|
||||
self.rawPositions[id] = $scope.configuration.panels[id];
|
||||
self.populatePosition(id);
|
||||
refreshComposition();
|
||||
});
|
||||
|
||||
// Layout may contain embedded views which will
|
||||
// listen for drops, so call preventDefault() so
|
||||
// that they can recognize that this event is handled.
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
//Will fetch fully contextualized composed objects, and populate
|
||||
// scope with them.
|
||||
function refreshComposition() {
|
||||
//Keep a track of how many composition callbacks have been made
|
||||
var thisCount = ++callbackCount;
|
||||
|
||||
$scope.domainObject.useCapability('composition').then(function (composition) {
|
||||
var ids;
|
||||
|
||||
//Is this callback for the most recent composition
|
||||
// request? If not, discard it. Prevents race condition
|
||||
if (thisCount === callbackCount) {
|
||||
ids = composition.map(function (object) {
|
||||
return object.getId();
|
||||
}) || [];
|
||||
|
||||
$scope.composition = composition;
|
||||
self.layoutPanels(ids);
|
||||
self.setFrames(ids);
|
||||
|
||||
if (self.selectedId &&
|
||||
self.selectedId !== $scope.domainObject.getId() &&
|
||||
composition.indexOf(self.selectedId) === -1) {
|
||||
// Click triggers selection of layout parent.
|
||||
self.$element[0].click();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// End drag; we don't want to put $scope into this
|
||||
// because it triggers "cpws" (copy window or scope)
|
||||
// errors in Angular.
|
||||
this.endDragInScope = function () {
|
||||
// Write to configuration; this is watched and
|
||||
// saved by the EditRepresenter.
|
||||
$scope.configuration =
|
||||
$scope.configuration || {};
|
||||
|
||||
$scope.configuration.panels =
|
||||
$scope.configuration.panels || {};
|
||||
|
||||
$scope.configuration.panels[self.activeDragId] =
|
||||
$scope.configuration.panels[self.activeDragId] || {};
|
||||
|
||||
$scope.configuration.panels[self.activeDragId].position =
|
||||
self.rawPositions[self.activeDragId].position;
|
||||
$scope.configuration.panels[self.activeDragId].dimensions =
|
||||
self.rawPositions[self.activeDragId].dimensions;
|
||||
|
||||
self.commit();
|
||||
};
|
||||
|
||||
// Sets the selectable object in response to the selection change event.
|
||||
function setSelection(selectable) {
|
||||
var selection = selectable[0];
|
||||
|
||||
if (!selection) {
|
||||
delete self.selectedId;
|
||||
return;
|
||||
}
|
||||
|
||||
self.selectedId = selection.context.oldItem.getId();
|
||||
self.drilledIn = undefined;
|
||||
self.selectable = selectable;
|
||||
}
|
||||
|
||||
this.positions = {};
|
||||
this.rawPositions = {};
|
||||
this.gridSize = DEFAULT_GRID_SIZE;
|
||||
this.$scope = $scope;
|
||||
this.drilledIn = undefined;
|
||||
this.openmct = openmct;
|
||||
|
||||
// Watch for changes to the grid size in the model
|
||||
$scope.$watch("model.layoutGrid", updateGridSize);
|
||||
|
||||
// Update composed objects on screen, and position panes
|
||||
$scope.$watchCollection("model.composition", refreshComposition);
|
||||
|
||||
openmct.selection.on('change', setSelection);
|
||||
|
||||
$scope.$on("$destroy", function () {
|
||||
openmct.selection.off("change", setSelection);
|
||||
self.unlisten();
|
||||
});
|
||||
|
||||
$scope.$on("mctDrop", handleDrop);
|
||||
|
||||
self.unlisten = self.$scope.domainObject.getCapability('mutation').listen(function (model) {
|
||||
$scope.configuration = model.configuration.layout;
|
||||
$scope.model = model;
|
||||
var panels = $scope.configuration.panels;
|
||||
|
||||
Object.keys(panels).forEach(function (key) {
|
||||
if (self.frames && self.frames.hasOwnProperty(key)) {
|
||||
self.frames[key] = panels[key].hasFrame;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Utility function to copy raw positions from configuration,
|
||||
// without writing directly to configuration (to avoid triggering
|
||||
// persistence from watchers during drags).
|
||||
function shallowCopy(obj, keys) {
|
||||
var copy = {};
|
||||
keys.forEach(function (k) {
|
||||
copy[k] = obj[k];
|
||||
});
|
||||
return copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the frames value. If a configuration panel has "hasFrame' property,
|
||||
* use that value, otherwise set a default value. A 'hyperlink' object should
|
||||
* have no frame by default.
|
||||
*
|
||||
* @param {string[]} ids the object ids
|
||||
* @private
|
||||
*/
|
||||
LayoutController.prototype.setFrames = function (ids) {
|
||||
var panels = shallowCopy(this.$scope.configuration.panels || {}, ids);
|
||||
this.frames = {};
|
||||
|
||||
this.$scope.composition.forEach(function (object) {
|
||||
var id = object.getId();
|
||||
panels[id] = panels[id] || {};
|
||||
|
||||
if (panels[id].hasOwnProperty('hasFrame')) {
|
||||
this.frames[id] = panels[id].hasFrame;
|
||||
} else {
|
||||
this.frames[id] = this.getDefaultFrame(object.getModel().type);
|
||||
}
|
||||
}, this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the default value for frame.
|
||||
*
|
||||
* @param type the domain object type
|
||||
* @return {boolean} true if the object should have
|
||||
* frame by default, false, otherwise
|
||||
*/
|
||||
LayoutController.prototype.getDefaultFrame = function (type) {
|
||||
return DEFAULT_HIDDEN_FRAME_TYPES.indexOf(type) === -1;
|
||||
};
|
||||
|
||||
// Convert from { positions: ..., dimensions: ... } to an
|
||||
// appropriate ng-style argument, to position frames.
|
||||
LayoutController.prototype.convertPosition = function (raw) {
|
||||
var gridSize = this.gridSize;
|
||||
// Multiply position/dimensions by grid size
|
||||
return {
|
||||
left: (gridSize[0] * raw.position[0]) + 'px',
|
||||
top: (gridSize[1] * raw.position[1]) + 'px',
|
||||
width: (gridSize[0] * raw.dimensions[0]) + 'px',
|
||||
height: (gridSize[1] * raw.dimensions[1]) + 'px',
|
||||
minWidth: (gridSize[0] * raw.dimensions[0]) + 'px',
|
||||
minHeight: (gridSize[1] * raw.dimensions[1]) + 'px'
|
||||
};
|
||||
};
|
||||
|
||||
// Generate default positions for a new panel
|
||||
LayoutController.prototype.defaultDimensions = function () {
|
||||
var gridSize = this.gridSize;
|
||||
return MINIMUM_FRAME_SIZE.map(function (min, i) {
|
||||
return Math.max(
|
||||
Math.ceil(min / gridSize[i]),
|
||||
DEFAULT_DIMENSIONS[i]
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
// Generate a default position (in its raw format) for a frame.
|
||||
// Use an index to ensure that default positions are unique.
|
||||
LayoutController.prototype.defaultPosition = function (index) {
|
||||
return {
|
||||
position: [index, index],
|
||||
dimensions: this.defaultDimensions()
|
||||
};
|
||||
};
|
||||
|
||||
// Store a computed position for a contained frame by its
|
||||
// domain object id. Called in a forEach loop, so arguments
|
||||
// are as expected there.
|
||||
LayoutController.prototype.populatePosition = function (id, index) {
|
||||
this.rawPositions[id] =
|
||||
this.rawPositions[id] || this.defaultPosition(index || 0);
|
||||
this.positions[id] =
|
||||
this.convertPosition(this.rawPositions[id]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a style object for a frame with the specified domain
|
||||
* object identifier, suitable for use in an `ng-style`
|
||||
* directive to position a frame as configured for this layout.
|
||||
* @param {string} id the object identifier
|
||||
* @returns {Object.<string, string>} an object with
|
||||
* appropriate left, width, etc fields for positioning
|
||||
*/
|
||||
LayoutController.prototype.getFrameStyle = function (id) {
|
||||
// Called in a loop, so just look up; the "positions"
|
||||
// object is kept up to date by a watch.
|
||||
return this.positions[id];
|
||||
};
|
||||
|
||||
/**
|
||||
* Start a drag gesture to move/resize a frame.
|
||||
*
|
||||
* The provided position and dimensions factors will determine
|
||||
* whether this is a move or a resize, and what type it
|
||||
* will be. For instance, a position factor of [1, 1]
|
||||
* will move a frame along with the mouse as the drag
|
||||
* proceeds, while a dimension factor of [0, 0] will leave
|
||||
* dimensions unchanged. Combining these in different
|
||||
* ways results in different handles; a position factor of
|
||||
* [1, 0] and a dimensions factor of [-1, 0] will implement
|
||||
* a left-edge resize, as the horizontal position will move
|
||||
* with the mouse while the horizontal dimensions shrink in
|
||||
* kind (and vertical properties remain unmodified.)
|
||||
*
|
||||
* @param {string} id the identifier of the domain object
|
||||
* in the frame being manipulated
|
||||
* @param {number[]} posFactor the position factor
|
||||
* @param {number[]} dimFactor the dimensions factor
|
||||
*/
|
||||
LayoutController.prototype.startDrag = function (id, posFactor, dimFactor) {
|
||||
this.activeDragId = id;
|
||||
this.activeDrag = new LayoutDrag(
|
||||
this.rawPositions[id],
|
||||
posFactor,
|
||||
dimFactor,
|
||||
this.gridSize
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Continue an active drag gesture.
|
||||
* @param {number[]} delta the offset, in pixels,
|
||||
* of the current pointer position, relative
|
||||
* to its position when the drag started
|
||||
*/
|
||||
LayoutController.prototype.continueDrag = function (delta) {
|
||||
if (this.activeDrag) {
|
||||
this.rawPositions[this.activeDragId] =
|
||||
this.activeDrag.getAdjustedPosition(delta);
|
||||
this.populatePosition(this.activeDragId);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Compute panel positions based on the layout's object model.
|
||||
* Defined as member function to facilitate testing.
|
||||
* @private
|
||||
*/
|
||||
LayoutController.prototype.layoutPanels = function (ids) {
|
||||
var configuration = this.$scope.configuration || {},
|
||||
self = this;
|
||||
|
||||
// Pull panel positions from configuration
|
||||
this.rawPositions =
|
||||
shallowCopy(configuration.panels || {}, ids);
|
||||
|
||||
// Clear prior computed positions
|
||||
this.positions = {};
|
||||
|
||||
// Update width/height that we are tracking
|
||||
this.gridSize =
|
||||
(this.$scope.model || {}).layoutGrid || DEFAULT_GRID_SIZE;
|
||||
|
||||
// Compute positions and add defaults where needed
|
||||
ids.forEach(function (id, index) {
|
||||
self.populatePosition(id, index);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* End the active drag gesture. This will update the
|
||||
* view configuration.
|
||||
*/
|
||||
LayoutController.prototype.endDrag = function () {
|
||||
this.dragInProgress = true;
|
||||
|
||||
setTimeout(function () {
|
||||
this.dragInProgress = false;
|
||||
}.bind(this), 0);
|
||||
|
||||
this.endDragInScope();
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if the object is currently selected.
|
||||
*
|
||||
* @param {string} obj the object to check for selection
|
||||
* @returns {boolean} true if selected, otherwise false
|
||||
*/
|
||||
LayoutController.prototype.selected = function (obj) {
|
||||
var sobj = this.openmct.selection.get()[0];
|
||||
return (sobj && sobj.context.oldItem.getId() === obj.getId()) ? true : false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Bypasses selection if drag is in progress.
|
||||
*
|
||||
* @param event the angular event object
|
||||
*/
|
||||
LayoutController.prototype.bypassSelection = function (event) {
|
||||
if (this.dragInProgress) {
|
||||
if (event) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if the domain object is drilled in.
|
||||
*
|
||||
* @param domainObject the domain object
|
||||
* @return true if the object is drilled in, false otherwise
|
||||
*/
|
||||
LayoutController.prototype.isDrilledIn = function (domainObject) {
|
||||
return this.drilledIn === domainObject.getId();
|
||||
};
|
||||
|
||||
/**
|
||||
* Puts the given object in the drilled-in mode.
|
||||
*
|
||||
* @param event the angular event object
|
||||
* @param domainObject the domain object
|
||||
*/
|
||||
LayoutController.prototype.drill = function (event, domainObject) {
|
||||
if (event) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
if (!domainObject.getCapability('editor').inEditContext()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!domainObject.hasCapability('composition')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Disable since fixed position doesn't use the selection API yet
|
||||
if (domainObject.getModel().type === 'telemetry.fixed') {
|
||||
return;
|
||||
}
|
||||
|
||||
this.drilledIn = domainObject.getId();
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the object has frame.
|
||||
*
|
||||
* @param {object} obj the object
|
||||
* @return {boolean} true if object has frame, otherwise false
|
||||
*/
|
||||
LayoutController.prototype.hasFrame = function (obj) {
|
||||
return this.frames[obj.getId()];
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the size of the grid, in pixels. The returned array
|
||||
* is in the form `[x, y]`.
|
||||
* @returns {number[]} the grid size
|
||||
*/
|
||||
LayoutController.prototype.getGridSize = function () {
|
||||
return this.gridSize;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the selection context.
|
||||
*
|
||||
* @param domainObject the domain object
|
||||
* @returns {object} the context object which includes item and oldItem
|
||||
*/
|
||||
LayoutController.prototype.getContext = function (domainObject) {
|
||||
return {
|
||||
item: domainObject.useCapability('adapter'),
|
||||
oldItem: domainObject
|
||||
};
|
||||
};
|
||||
|
||||
LayoutController.prototype.commit = function () {
|
||||
var model = this.$scope.model;
|
||||
model.configuration = model.configuration || {};
|
||||
model.configuration.layout = this.$scope.configuration;
|
||||
|
||||
this.$scope.domainObject.useCapability('mutation', function () {
|
||||
return model;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Selects a newly-dropped object.
|
||||
*
|
||||
* @param classSelector the css class selector
|
||||
* @param domainObject the domain object
|
||||
*/
|
||||
LayoutController.prototype.selectIfNew = function (selector, domainObject) {
|
||||
if (domainObject.getId() === this.droppedIdToSelectAfterRefresh) {
|
||||
setTimeout(function () {
|
||||
$('[data-layout-id="' + selector + '"]')[0].click();
|
||||
delete this.droppedIdToSelectAfterRefresh;
|
||||
}.bind(this), 0);
|
||||
}
|
||||
};
|
||||
|
||||
return LayoutController;
|
||||
}
|
||||
);
|
||||
|
@ -1,479 +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(
|
||||
[
|
||||
"../src/LayoutController",
|
||||
"zepto"
|
||||
],
|
||||
function (
|
||||
LayoutController,
|
||||
$
|
||||
) {
|
||||
|
||||
describe("The Layout controller", function () {
|
||||
var mockScope,
|
||||
mockEvent,
|
||||
testModel,
|
||||
testConfiguration,
|
||||
controller,
|
||||
mockCompositionCapability,
|
||||
mockComposition,
|
||||
mockCompositionObjects,
|
||||
mockOpenMCT,
|
||||
mockSelection,
|
||||
mockDomainObjectCapability,
|
||||
mockObjects,
|
||||
unlistenFunc,
|
||||
$element = [],
|
||||
selectable = [];
|
||||
|
||||
function mockPromise(value) {
|
||||
return {
|
||||
then: function (thenFunc) {
|
||||
return mockPromise(thenFunc(value));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function mockDomainObject(id) {
|
||||
return {
|
||||
getId: function () {
|
||||
return id;
|
||||
},
|
||||
useCapability: function () {
|
||||
return mockCompositionCapability;
|
||||
},
|
||||
getModel: function () {
|
||||
if (id === 'b') {
|
||||
return {
|
||||
type : 'hyperlink'
|
||||
};
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
getCapability: function () {
|
||||
return mockDomainObjectCapability;
|
||||
},
|
||||
hasCapability: function (param) {
|
||||
if (param === 'composition') {
|
||||
return id !== 'b';
|
||||
}
|
||||
},
|
||||
type: "testType"
|
||||
};
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
mockScope = jasmine.createSpyObj(
|
||||
"$scope",
|
||||
["$watch", "$watchCollection", "$on"]
|
||||
);
|
||||
mockEvent = jasmine.createSpyObj(
|
||||
'event',
|
||||
['preventDefault', 'stopPropagation']
|
||||
);
|
||||
|
||||
testModel = {};
|
||||
|
||||
mockComposition = ["a", "b", "c"];
|
||||
mockCompositionObjects = mockComposition.map(mockDomainObject);
|
||||
|
||||
testConfiguration = {
|
||||
panels: {
|
||||
a: {
|
||||
position: [20, 10],
|
||||
dimensions: [5, 5]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
unlistenFunc = jasmine.createSpy("unlisten");
|
||||
mockDomainObjectCapability = jasmine.createSpyObj('capability',
|
||||
['inEditContext', 'listen']
|
||||
);
|
||||
mockDomainObjectCapability.listen.and.returnValue(unlistenFunc);
|
||||
|
||||
mockCompositionCapability = mockPromise(mockCompositionObjects);
|
||||
|
||||
mockScope.domainObject = mockDomainObject("mockDomainObject");
|
||||
mockScope.model = testModel;
|
||||
mockScope.configuration = testConfiguration;
|
||||
|
||||
selectable[0] = {
|
||||
context: {
|
||||
oldItem: mockScope.domainObject
|
||||
}
|
||||
};
|
||||
|
||||
mockSelection = jasmine.createSpyObj("selection", [
|
||||
'select',
|
||||
'on',
|
||||
'off',
|
||||
'get'
|
||||
]);
|
||||
mockSelection.get.and.returnValue(selectable);
|
||||
|
||||
mockObjects = jasmine.createSpyObj('objects', [
|
||||
'get'
|
||||
]);
|
||||
mockObjects.get.and.returnValue(mockPromise(mockDomainObject("mockObject")));
|
||||
mockOpenMCT = {
|
||||
selection: mockSelection,
|
||||
objects: mockObjects
|
||||
};
|
||||
|
||||
$element = $('<div></div>');
|
||||
$(document).find('body').append($element);
|
||||
spyOn($element[0], 'click');
|
||||
|
||||
spyOn(mockScope.domainObject, "useCapability").and.callThrough();
|
||||
|
||||
controller = new LayoutController(mockScope, $element, mockOpenMCT);
|
||||
spyOn(controller, "layoutPanels").and.callThrough();
|
||||
spyOn(controller, "commit");
|
||||
|
||||
jasmine.clock().install();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
$element.remove();
|
||||
jasmine.clock().uninstall();
|
||||
});
|
||||
|
||||
|
||||
it("listens for selection change events", function () {
|
||||
expect(mockOpenMCT.selection.on).toHaveBeenCalledWith(
|
||||
'change',
|
||||
jasmine.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
it("cleans up on scope destroy", function () {
|
||||
expect(mockScope.$on).toHaveBeenCalledWith(
|
||||
'$destroy',
|
||||
jasmine.any(Function)
|
||||
);
|
||||
|
||||
mockScope.$on.calls.all()[0].args[1]();
|
||||
|
||||
expect(mockOpenMCT.selection.off).toHaveBeenCalledWith(
|
||||
'change',
|
||||
jasmine.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
// Model changes will indicate that panel positions
|
||||
// may have changed, for instance.
|
||||
it("watches for changes to composition", function () {
|
||||
expect(mockScope.$watchCollection).toHaveBeenCalledWith(
|
||||
"model.composition",
|
||||
jasmine.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
it("Retrieves updated composition from composition capability", function () {
|
||||
mockScope.$watchCollection.calls.mostRecent().args[1]();
|
||||
expect(mockScope.domainObject.useCapability).toHaveBeenCalledWith(
|
||||
"composition"
|
||||
);
|
||||
expect(controller.layoutPanels).toHaveBeenCalledWith(
|
||||
mockComposition
|
||||
);
|
||||
});
|
||||
|
||||
it("Is robust to concurrent changes to composition", function () {
|
||||
var secondMockComposition = ["a", "b", "c", "d"],
|
||||
secondMockCompositionObjects = secondMockComposition.map(mockDomainObject),
|
||||
firstCompositionCB,
|
||||
secondCompositionCB;
|
||||
|
||||
spyOn(mockCompositionCapability, "then");
|
||||
mockScope.$watchCollection.calls.mostRecent().args[1]();
|
||||
mockScope.$watchCollection.calls.mostRecent().args[1]();
|
||||
|
||||
firstCompositionCB = mockCompositionCapability.then.calls.all()[0].args[0];
|
||||
secondCompositionCB = mockCompositionCapability.then.calls.all()[1].args[0];
|
||||
|
||||
//Resolve promises in reverse order
|
||||
secondCompositionCB(secondMockCompositionObjects);
|
||||
firstCompositionCB(mockCompositionObjects);
|
||||
|
||||
//Expect the promise call that was initiated most recently to
|
||||
// be the one used to populate scope, irrespective of order that
|
||||
// it was eventually resolved
|
||||
expect(mockScope.composition).toBe(secondMockCompositionObjects);
|
||||
});
|
||||
|
||||
|
||||
it("provides styles for frames, from configuration", function () {
|
||||
mockScope.$watchCollection.calls.mostRecent().args[1]();
|
||||
expect(controller.getFrameStyle("a")).toEqual({
|
||||
top: "320px",
|
||||
left: "640px",
|
||||
width: "160px",
|
||||
height: "160px",
|
||||
minWidth : '160px',
|
||||
minHeight : '160px'
|
||||
});
|
||||
});
|
||||
|
||||
it("provides default styles for frames", function () {
|
||||
var styleB, styleC;
|
||||
|
||||
// b and c do not have configured positions
|
||||
mockScope.$watchCollection.calls.mostRecent().args[1]();
|
||||
|
||||
styleB = controller.getFrameStyle("b");
|
||||
styleC = controller.getFrameStyle("c");
|
||||
|
||||
// Should have a position, but we don't care what
|
||||
expect(styleB.left).toBeDefined();
|
||||
expect(styleB.top).toBeDefined();
|
||||
expect(styleC.left).toBeDefined();
|
||||
expect(styleC.top).toBeDefined();
|
||||
|
||||
// Should have ensured some difference in position
|
||||
expect(styleB).not.toEqual(styleC);
|
||||
});
|
||||
|
||||
it("allows panels to be dragged", function () {
|
||||
// Populate scope
|
||||
mockScope.$watchCollection.calls.mostRecent().args[1]();
|
||||
|
||||
// Verify precondition
|
||||
expect(testConfiguration.panels.b).not.toBeDefined();
|
||||
|
||||
// Do a drag
|
||||
controller.startDrag("b", [1, 1], [0, 0]);
|
||||
controller.continueDrag([100, 100]);
|
||||
controller.endDrag();
|
||||
|
||||
// We do not look closely at the details here;
|
||||
// that is tested in LayoutDragSpec. Just make sure
|
||||
// that a configuration for b has been defined.
|
||||
expect(testConfiguration.panels.b).toBeDefined();
|
||||
});
|
||||
|
||||
|
||||
it("invokes commit after drag", function () {
|
||||
// Populate scope
|
||||
mockScope.$watchCollection.calls.mostRecent().args[1]();
|
||||
|
||||
// Do a drag
|
||||
controller.startDrag("b", [1, 1], [0, 0]);
|
||||
controller.continueDrag([100, 100]);
|
||||
controller.endDrag();
|
||||
|
||||
expect(controller.commit).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("listens for drop events", function () {
|
||||
// Layout should position panels according to
|
||||
// where the user dropped them, so it needs to
|
||||
// listen for drop events.
|
||||
expect(mockScope.$on).toHaveBeenCalledWith(
|
||||
'mctDrop',
|
||||
jasmine.any(Function)
|
||||
);
|
||||
|
||||
// Verify precondition
|
||||
expect(testConfiguration.panels.d).not.toBeDefined();
|
||||
|
||||
// Notify that a drop occurred
|
||||
mockScope.$on.calls.mostRecent().args[1](
|
||||
mockEvent,
|
||||
'd',
|
||||
{ x: 300, y: 100 }
|
||||
);
|
||||
expect(testConfiguration.panels.d).toBeDefined();
|
||||
expect(mockEvent.preventDefault).toHaveBeenCalled();
|
||||
expect(controller.commit).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("ignores drops when default has been prevented", function () {
|
||||
// Avoids redundant drop-handling, WTD-1233
|
||||
mockEvent.defaultPrevented = true;
|
||||
|
||||
// Notify that a drop occurred
|
||||
mockScope.$on.calls.mostRecent().args[1](
|
||||
mockEvent,
|
||||
'd',
|
||||
{ x: 300, y: 100 }
|
||||
);
|
||||
expect(testConfiguration.panels.d).not.toBeDefined();
|
||||
});
|
||||
|
||||
it("ensures a minimum frame size", function () {
|
||||
var styleB;
|
||||
|
||||
// Start with a very small frame size
|
||||
testModel.layoutGrid = [1, 1];
|
||||
|
||||
// White-boxy; we know which watch is which
|
||||
mockScope.$watch.calls.all()[0].args[1](testModel.layoutGrid);
|
||||
mockScope.$watchCollection.calls.all()[0].args[1](testModel.composition);
|
||||
|
||||
styleB = controller.getFrameStyle("b");
|
||||
|
||||
// Resulting size should still be reasonably large pixel-size
|
||||
expect(parseInt(styleB.width, 10)).toBeGreaterThan(63);
|
||||
expect(parseInt(styleB.width, 10)).toBeGreaterThan(31);
|
||||
});
|
||||
|
||||
it("ensures a minimum frame size on drop", function () {
|
||||
var style;
|
||||
|
||||
// Start with a very small frame size
|
||||
testModel.layoutGrid = [1, 1];
|
||||
mockScope.$watch.calls.all()[0].args[1](testModel.layoutGrid);
|
||||
|
||||
// Add a new object to the composition
|
||||
mockComposition = ["a", "b", "c", "d"];
|
||||
mockCompositionObjects = mockComposition.map(mockDomainObject);
|
||||
mockCompositionCapability = mockPromise(mockCompositionObjects);
|
||||
|
||||
// Notify that a drop occurred
|
||||
mockScope.$on.calls.mostRecent().args[1](
|
||||
mockEvent,
|
||||
'd',
|
||||
{ x: 300, y: 100 }
|
||||
);
|
||||
|
||||
style = controller.getFrameStyle("d");
|
||||
|
||||
// Resulting size should still be reasonably large pixel-size
|
||||
expect(parseInt(style.width, 10)).toBeGreaterThan(63);
|
||||
expect(parseInt(style.height, 10)).toBeGreaterThan(31);
|
||||
});
|
||||
|
||||
it("updates positions of existing objects on a drop", function () {
|
||||
var oldStyle;
|
||||
|
||||
mockScope.$watchCollection.calls.mostRecent().args[1]();
|
||||
|
||||
oldStyle = controller.getFrameStyle("b");
|
||||
|
||||
expect(oldStyle).toBeDefined();
|
||||
|
||||
// ...drop event...
|
||||
mockScope.$on.calls.mostRecent()
|
||||
.args[1](mockEvent, 'b', { x: 300, y: 100 });
|
||||
|
||||
expect(controller.getFrameStyle("b"))
|
||||
.not.toEqual(oldStyle);
|
||||
});
|
||||
|
||||
it("allows objects to be selected", function () {
|
||||
mockScope.$watchCollection.calls.mostRecent().args[1]();
|
||||
var childObj = mockCompositionObjects[0];
|
||||
selectable[0].context.oldItem = childObj;
|
||||
mockOpenMCT.selection.on.calls.mostRecent().args[1](selectable);
|
||||
|
||||
expect(controller.selected(childObj)).toBe(true);
|
||||
});
|
||||
|
||||
it("prevents event bubbling while drag is in progress", function () {
|
||||
mockScope.$watchCollection.calls.mostRecent().args[1]();
|
||||
var childObj = mockCompositionObjects[0];
|
||||
|
||||
// Do a drag
|
||||
controller.startDrag(childObj.getId(), [1, 1], [0, 0]);
|
||||
controller.continueDrag([100, 100]);
|
||||
controller.endDrag();
|
||||
|
||||
// Because mouse position could cause the parent object to be selected, this should be ignored.
|
||||
controller.bypassSelection(mockEvent);
|
||||
|
||||
expect(mockEvent.stopPropagation).toHaveBeenCalled();
|
||||
|
||||
// Shoud be able to select another object when dragging is done.
|
||||
jasmine.clock().tick(0);
|
||||
mockEvent.stopPropagation.calls.reset();
|
||||
controller.bypassSelection(mockEvent);
|
||||
|
||||
expect(mockEvent.stopPropagation).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("shows frames by default", function () {
|
||||
mockScope.$watchCollection.calls.mostRecent().args[1]();
|
||||
|
||||
expect(controller.hasFrame(mockCompositionObjects[0])).toBe(true);
|
||||
});
|
||||
|
||||
it("hyperlinks hide frame by default", function () {
|
||||
mockScope.$watchCollection.calls.mostRecent().args[1]();
|
||||
|
||||
expect(controller.hasFrame(mockCompositionObjects[1])).toBe(false);
|
||||
});
|
||||
|
||||
it("selects the parent object when selected object is removed", function () {
|
||||
mockScope.$watchCollection.calls.mostRecent().args[1]();
|
||||
var childObj = mockCompositionObjects[0];
|
||||
selectable[0].context.oldItem = childObj;
|
||||
mockOpenMCT.selection.on.calls.mostRecent().args[1](selectable);
|
||||
|
||||
var composition = ["b", "c"];
|
||||
mockScope.$watchCollection.calls.mostRecent().args[1](composition);
|
||||
|
||||
expect($element[0].click).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("allows objects to be drilled-in only when editing", function () {
|
||||
mockScope.$watchCollection.calls.mostRecent().args[1]();
|
||||
var childObj = mockCompositionObjects[0];
|
||||
childObj.getCapability().inEditContext.and.returnValue(false);
|
||||
controller.drill(mockEvent, childObj);
|
||||
|
||||
expect(controller.isDrilledIn(childObj)).toBe(false);
|
||||
});
|
||||
|
||||
it("allows objects to be drilled-in only if it has sub objects", function () {
|
||||
mockScope.$watchCollection.calls.mostRecent().args[1]();
|
||||
var childObj = mockCompositionObjects[1];
|
||||
childObj.getCapability().inEditContext.and.returnValue(true);
|
||||
controller.drill(mockEvent, childObj);
|
||||
|
||||
expect(controller.isDrilledIn(childObj)).toBe(false);
|
||||
});
|
||||
|
||||
it("selects a newly-dropped object", function () {
|
||||
mockScope.$on.calls.mostRecent().args[1](
|
||||
mockEvent,
|
||||
'd',
|
||||
{ x: 300, y: 100 }
|
||||
);
|
||||
|
||||
var childObj = mockDomainObject("d");
|
||||
var testElement = $("<div data-layout-id='some-id'></div>");
|
||||
$element.append(testElement);
|
||||
spyOn(testElement[0], 'click');
|
||||
|
||||
controller.selectIfNew('some-id', childObj);
|
||||
jasmine.clock().tick(0);
|
||||
|
||||
expect(testElement[0].click).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
19
src/MCT.js
19
src/MCT.js
@ -40,6 +40,7 @@ define([
|
||||
'./styles-new/core.scss',
|
||||
'./styles-new/notebook.scss',
|
||||
'./ui/components/layout/Layout.vue',
|
||||
'./ui/overlayService/overlayService',
|
||||
'vue'
|
||||
], function (
|
||||
EventEmitter,
|
||||
@ -61,6 +62,7 @@ define([
|
||||
coreStyles,
|
||||
NotebookStyles,
|
||||
Layout,
|
||||
OverlayService,
|
||||
Vue
|
||||
) {
|
||||
/**
|
||||
@ -221,11 +223,18 @@ define([
|
||||
*/
|
||||
this.indicators = new api.IndicatorAPI(this);
|
||||
|
||||
this.notifications = new api.NotificationAPI();
|
||||
|
||||
this.Dialog = api.Dialog;
|
||||
|
||||
this.editor = new api.EditorAPI.default(this);
|
||||
|
||||
this.OverlayService = new OverlayService();
|
||||
|
||||
this.legacyRegistry = defaultRegistry;
|
||||
this.install(this.plugins.Plot());
|
||||
this.install(this.plugins.TelemetryTable());
|
||||
this.install(this.plugins.DisplayLayout());
|
||||
|
||||
if (typeof BUILD_CONSTANTS !== 'undefined') {
|
||||
this.install(buildInfoPlugin(BUILD_CONSTANTS));
|
||||
@ -310,13 +319,17 @@ define([
|
||||
this.$injector.get('objectService');
|
||||
|
||||
var appLayout = new Vue({
|
||||
mixins: [Layout.default],
|
||||
components: {
|
||||
'Layout': Layout.default
|
||||
},
|
||||
provide: {
|
||||
openmct: this
|
||||
}
|
||||
},
|
||||
template: '<Layout ref="layout"></Layout>'
|
||||
});
|
||||
domElement.appendChild(appLayout.$mount().$el);
|
||||
this.layout = appLayout;
|
||||
|
||||
this.layout = appLayout.$refs.layout;
|
||||
Browse(this);
|
||||
this.router.start();
|
||||
this.emit('start');
|
||||
|
95
src/adapter/views/TypeInspectorViewProvider.js
Normal file
95
src/adapter/views/TypeInspectorViewProvider.js
Normal file
@ -0,0 +1,95 @@
|
||||
define([
|
||||
|
||||
], function (
|
||||
|
||||
) {
|
||||
const DEFAULT_VIEW_PRIORITY = 100;
|
||||
|
||||
const PRIORITY_LEVELS = {
|
||||
"fallback": Number.NEGATIVE_INFINITY,
|
||||
"default": -100,
|
||||
"none": 0,
|
||||
"optional": DEFAULT_VIEW_PRIORITY,
|
||||
"preferred": 1000,
|
||||
"mandatory": Number.POSITIVE_INFINITY
|
||||
};
|
||||
|
||||
function TypeInspectorViewProvider(typeDefinition, openmct, convertToLegacyObject) {
|
||||
console.warn(`DEPRECATION WARNING: Migrate ${typeDefinition.key} from ${typeDefinition.bundle.path} to use the new Inspector View APIs. Legacy Inspector view support will be removed soon.`);
|
||||
let representation = openmct.$injector.get('representations[]')
|
||||
.filter((r) => r.key === typeDefinition.inspector)[0];
|
||||
|
||||
return {
|
||||
key: representation.key,
|
||||
name: representation.name,
|
||||
cssClass: representation.cssClass,
|
||||
description: representation.description,
|
||||
canView: function (selection) {
|
||||
if (!selection[0] || !selection[0].context.item) {
|
||||
return false;
|
||||
}
|
||||
let domainObject = selection[0].context.item;
|
||||
return domainObject.type === typeDefinition.key;
|
||||
},
|
||||
view: function (selection) {
|
||||
let domainObject = selection[0].context.item;
|
||||
let $rootScope = openmct.$injector.get('$rootScope');
|
||||
let templateLinker = openmct.$injector.get('templateLinker');
|
||||
let scope = $rootScope.$new();
|
||||
let legacyObject = convertToLegacyObject(domainObject);
|
||||
let isDestroyed = false;
|
||||
scope.domainObject = legacyObject;
|
||||
scope.model = legacyObject.getModel();
|
||||
|
||||
|
||||
return {
|
||||
show: function (container) {
|
||||
// TODO: implement "gestures" support ?
|
||||
let uses = representation.uses || [];
|
||||
let promises = [];
|
||||
let results = uses.map(function (capabilityKey, i) {
|
||||
let result = legacyObject.useCapability(capabilityKey);
|
||||
if (result.then) {
|
||||
promises.push(result.then(function (r) {
|
||||
results[i] = r;
|
||||
}));
|
||||
}
|
||||
return result;
|
||||
});
|
||||
|
||||
function link() {
|
||||
if (isDestroyed) {
|
||||
return;
|
||||
}
|
||||
uses.forEach(function (key, i) {
|
||||
scope[key] = results[i];
|
||||
});
|
||||
templateLinker.link(
|
||||
scope,
|
||||
openmct.$angular.element(container),
|
||||
representation
|
||||
);
|
||||
container.style.height = '100%';
|
||||
}
|
||||
|
||||
if (promises.length) {
|
||||
Promise.all(promises)
|
||||
.then(function () {
|
||||
link();
|
||||
scope.$digest();
|
||||
});
|
||||
} else {
|
||||
link();
|
||||
}
|
||||
},
|
||||
destroy: function () {
|
||||
scope.$destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
return TypeInspectorViewProvider;
|
||||
|
||||
});
|
@ -1,8 +1,10 @@
|
||||
define([
|
||||
'./LegacyViewProvider',
|
||||
'./TypeInspectorViewProvider',
|
||||
'../../api/objects/object-utils'
|
||||
], function (
|
||||
LegacyViewProvider,
|
||||
TypeInspectorViewProvider,
|
||||
objectUtils
|
||||
) {
|
||||
function installLegacyViews(openmct, legacyViews, instantiate) {
|
||||
@ -16,6 +18,13 @@ define([
|
||||
legacyViews.forEach(function (legacyView) {
|
||||
openmct.objectViews.addProvider(new LegacyViewProvider(legacyView, openmct, convertToLegacyObject));
|
||||
});
|
||||
|
||||
let inspectorTypes = openmct.$injector.get('types[]')
|
||||
.filter((t) => t.hasOwnProperty('inspector'));
|
||||
|
||||
inspectorTypes.forEach(function (typeDefinition) {
|
||||
openmct.inspectorViews.addProvider(new TypeInspectorViewProvider(typeDefinition, openmct, convertToLegacyObject));
|
||||
});
|
||||
}
|
||||
|
||||
return installLegacyViews;
|
||||
|
83
src/api/Editor.js
Normal file
83
src/api/Editor.js
Normal file
@ -0,0 +1,83 @@
|
||||
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
import EventEmitter from 'EventEmitter';
|
||||
|
||||
export default class Editor extends EventEmitter {
|
||||
constructor(openmct) {
|
||||
super();
|
||||
this.editing = false;
|
||||
this.openmct = openmct;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate an editing session. This will start a transaction during
|
||||
* which any persist operations will be deferred until either save()
|
||||
* or finish() are called.
|
||||
*/
|
||||
edit() {
|
||||
this.editing = true;
|
||||
this.getTransactionService().startTransaction();
|
||||
this.emit('isEditing', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns true if the application is in edit mode, false otherwise.
|
||||
*/
|
||||
isEditing() {
|
||||
return this.editing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save any unsaved changes from this editing session. This will
|
||||
* end the current transaction.
|
||||
*/
|
||||
save() {
|
||||
return this.getTransactionService().commit().then((result)=>{
|
||||
this.editing = false;
|
||||
this.emit('isEditing', false);
|
||||
return result
|
||||
}).catch((error)=>{
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* End the currently active transaction and discard unsaved changes.
|
||||
*/
|
||||
cancel() {
|
||||
this.getTransactionService().cancel();
|
||||
this.editing = false;
|
||||
this.emit('isEditing', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
getTransactionService() {
|
||||
if (!this.transactionService) {
|
||||
this.transactionService = this.openmct.$injector.get('transactionService');
|
||||
}
|
||||
return this.transactionService;
|
||||
}
|
||||
}
|
@ -28,7 +28,10 @@ define([
|
||||
'./ui/Dialog',
|
||||
'./ui/GestureAPI',
|
||||
'./telemetry/TelemetryAPI',
|
||||
'./indicators/IndicatorAPI'
|
||||
'./indicators/IndicatorAPI',
|
||||
'./notifications/NotificationAPI',
|
||||
'./Editor'
|
||||
|
||||
], function (
|
||||
TimeAPI,
|
||||
ObjectAPI,
|
||||
@ -37,7 +40,9 @@ define([
|
||||
Dialog,
|
||||
GestureAPI,
|
||||
TelemetryAPI,
|
||||
IndicatorAPI
|
||||
IndicatorAPI,
|
||||
NotificationAPI,
|
||||
EditorAPI
|
||||
) {
|
||||
return {
|
||||
TimeAPI: TimeAPI,
|
||||
@ -47,6 +52,8 @@ define([
|
||||
TypeRegistry: TypeRegistry,
|
||||
GestureAPI: GestureAPI,
|
||||
TelemetryAPI: TelemetryAPI,
|
||||
IndicatorAPI: IndicatorAPI
|
||||
IndicatorAPI: IndicatorAPI,
|
||||
NotificationAPI: NotificationAPI.default,
|
||||
EditorAPI: EditorAPI
|
||||
};
|
||||
});
|
||||
|
@ -28,7 +28,7 @@ define([
|
||||
) {
|
||||
function IndicatorAPI(openmct) {
|
||||
this.openmct = openmct;
|
||||
this.indicatorElements = [];
|
||||
this.indicatorObjects = [];
|
||||
}
|
||||
|
||||
IndicatorAPI.prototype.simpleIndicator = function () {
|
||||
@ -55,12 +55,7 @@ define([
|
||||
*
|
||||
*/
|
||||
IndicatorAPI.prototype.add = function (indicator) {
|
||||
// So that we can consistently position indicator elements,
|
||||
// guarantee that they are wrapped in an element we control
|
||||
var wrapperNode = document.createElement('div');
|
||||
wrapperNode.className = 'h-indicator';
|
||||
wrapperNode.appendChild(indicator.element);
|
||||
this.indicatorElements.push(wrapperNode);
|
||||
this.indicatorObjects.push(indicator);
|
||||
};
|
||||
|
||||
return IndicatorAPI;
|
||||
|
48
src/api/notifications/MCTNotification.js
Normal file
48
src/api/notifications/MCTNotification.js
Normal file
@ -0,0 +1,48 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
import EventEmitter from 'EventEmitter';
|
||||
export default class MCTNotification extends EventEmitter {
|
||||
|
||||
constructor(notificationModel, notificationAPI) {
|
||||
super();
|
||||
this.notifications = notificationAPI;
|
||||
this.model = notificationModel;
|
||||
this.initializeModel();
|
||||
}
|
||||
|
||||
minimize() {
|
||||
this.notifications.minimize(this);
|
||||
}
|
||||
|
||||
dismiss() {
|
||||
this.notifications.dismiss(this)
|
||||
}
|
||||
|
||||
dismissOrMinimize() {
|
||||
this.notifications.dismissOrMinimize(this);
|
||||
}
|
||||
|
||||
initializeModel() {
|
||||
this.model.minimized = this.model.minimized || false;
|
||||
}
|
||||
}
|
353
src/api/notifications/NotificationApi.js
Normal file
353
src/api/notifications/NotificationApi.js
Normal file
@ -0,0 +1,353 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* This bundle implements the notification service, which can be used to
|
||||
* show banner notifications to the user. Banner notifications
|
||||
* are used to inform users of events in a non-intrusive way. As
|
||||
* much as possible, notifications share a model with blocking
|
||||
* dialogs so that the same information can be provided in a dialog
|
||||
* and then minimized to a banner notification if needed.
|
||||
*
|
||||
* @namespace platform/api/notifications
|
||||
*/
|
||||
import moment from 'moment';
|
||||
import EventEmitter from 'EventEmitter';
|
||||
import MCTNotification from './MCTNotification.js';
|
||||
|
||||
/**
|
||||
* A representation of a banner notification. Banner notifications
|
||||
* are used to inform users of events in a non-intrusive way. As
|
||||
* much as possible, notifications share a model with blocking
|
||||
* dialogs so that the same information can be provided in a dialog
|
||||
* and then minimized to a banner notification if needed, or vice-versa.
|
||||
*
|
||||
* @typedef {object} NotificationModel
|
||||
* @property {string} title The title of the message
|
||||
* @property {string} severity The importance of the message (one of
|
||||
* 'info', 'alert', or 'error' where info < alert <error)
|
||||
* @property {number} [progress] The completion status of a task
|
||||
* represented numerically
|
||||
* @property {boolean} [unknownProgress] a boolean indicating that the
|
||||
* progress of the underlying task is unknown. This will result in a
|
||||
* visually distinct progress bar.
|
||||
* @property {boolean} [autoDismiss] If truthy, dialog will
|
||||
* be automatically minimized or dismissed (depending on severity).
|
||||
* Additionally, if the provided value is a number, it will be used
|
||||
* as the delay period before being dismissed.
|
||||
* @property {boolean} [dismissable=true] If true, notification will
|
||||
* include an option to dismiss it completely.
|
||||
* @see DialogModel
|
||||
*/
|
||||
|
||||
const DEFAULT_AUTO_DISMISS_TIMEOUT = 3000;
|
||||
const MINIMIZE_ANIMATION_TIMEOUT = 300;
|
||||
|
||||
/**
|
||||
* The notification service is responsible for informing the user of
|
||||
* events via the use of banner notifications.
|
||||
* @memberof platform/commonUI/notification
|
||||
* @constructor
|
||||
* @param defaultAutoDismissTimeout The period of time that an
|
||||
* auto-dismissed message will be displayed for.
|
||||
* @param minimizeAnimationTimeout When notifications are minimized, a brief
|
||||
* animation is shown. This animation requires some time to execute,
|
||||
* so a timeout is required before the notification is hidden
|
||||
*/
|
||||
export default class NotificationAPI extends EventEmitter {
|
||||
constructor() {
|
||||
super();
|
||||
this.notifications = [];
|
||||
this.highest = { severity: "info" };
|
||||
|
||||
/*
|
||||
* A context in which to hold the active notification and a
|
||||
* handle to its timeout.
|
||||
*/
|
||||
this.activeNotification = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Minimize a notification. The notification will still be available
|
||||
* from the notification list. Typically notifications with a
|
||||
* severity of 'info' should not be minimized, but rather
|
||||
* dismissed. If you're not sure which is appropriate,
|
||||
* use {@link Notification#dismissOrMinimize}
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
minimize(notification) {
|
||||
//Check this is a known notification
|
||||
let index = this.notifications.indexOf(notification);
|
||||
|
||||
if (this.activeTimeout) {
|
||||
/*
|
||||
Method can be called manually (clicking dismiss) or
|
||||
automatically from an auto-timeout. this.activeTimeout
|
||||
acts as a semaphore to prevent race conditions. Cancel any
|
||||
timeout in progress (for the case where a manual dismiss
|
||||
has shortcut an active auto-dismiss), and clear the
|
||||
semaphore.
|
||||
*/
|
||||
clearTimeout(this.activeTimeout);
|
||||
delete this.activeTimeout;
|
||||
}
|
||||
|
||||
if (index >= 0) {
|
||||
notification.model.minimized = true;
|
||||
//Add a brief timeout before showing the next notification
|
||||
// in order to allow the minimize animation to run through.
|
||||
setTimeout(() => {
|
||||
notification.emit('destroy');
|
||||
this.setActiveNotification(this.selectNextNotification());
|
||||
}, MINIMIZE_ANIMATION_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Completely removes a notification. This will dismiss it from the
|
||||
* message banner and remove it from the list of notifications.
|
||||
* Typically only notifications with a severity of info should be
|
||||
* dismissed. If you're not sure whether to dismiss or minimize a
|
||||
* notification, use {@link Notification#dismissOrMinimize}.
|
||||
* dismiss
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
dismiss(notification) {
|
||||
//Check this is a known notification
|
||||
let index = this.notifications.indexOf(notification);
|
||||
|
||||
if (this.activeTimeout) {
|
||||
/* Method can be called manually (clicking dismiss) or
|
||||
* automatically from an auto-timeout. this.activeTimeout
|
||||
* acts as a semaphore to prevent race conditions. Cancel any
|
||||
* timeout in progress (for the case where a manual dismiss
|
||||
* has shortcut an active auto-dismiss), and clear the
|
||||
* semaphore.
|
||||
*/
|
||||
|
||||
clearTimeout(this.activeTimeout);
|
||||
delete this.activeTimeout;
|
||||
}
|
||||
|
||||
if (index >= 0) {
|
||||
this.notifications.splice(index, 1);
|
||||
}
|
||||
this.setActiveNotification(this.selectNextNotification());
|
||||
this.setHighestSeverity();
|
||||
notification.emit('destroy');
|
||||
}
|
||||
|
||||
/**
|
||||
* Depending on the severity of the notification will selectively
|
||||
* dismiss or minimize where appropriate.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
dismissOrMinimize(notification) {
|
||||
let model = notification.model;
|
||||
if (model.severity === "info") {
|
||||
if (model.autoDismiss === false) {
|
||||
this.minimize(notification);
|
||||
} else {
|
||||
this.dismiss(notification);
|
||||
}
|
||||
} else {
|
||||
this.minimize(notification);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the notification that is currently visible in the banner area
|
||||
* @returns {Notification}
|
||||
*/
|
||||
getActiveNotification() {
|
||||
return this.activeNotification;
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience method for info notifications. Notifications
|
||||
* created via this method will be auto-destroy after a default
|
||||
* wait period unless explicitly forbidden by the caller through
|
||||
* the {autoDismiss} property on the {NotificationModel}, in which
|
||||
* case the notification will be minimized after the wait.
|
||||
* @param {NotificationModel | string} message either a string for
|
||||
* the title of the notification message, or a {@link NotificationModel}
|
||||
* defining the options notification to display
|
||||
* @returns {Notification} the provided notification decorated with
|
||||
* functions to dismiss or minimize
|
||||
*/
|
||||
info(message) {
|
||||
let notificationModel = typeof message === "string" ? {title: message} : message;
|
||||
notificationModel.severity = "info";
|
||||
return this.notify(notificationModel);
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience method for alert notifications. Notifications
|
||||
* created via this method will will have severity of "alert" enforced
|
||||
* @param {NotificationModel | string} message either a string for
|
||||
* the title of the alert message with default options, or a
|
||||
* {@link NotificationModel} defining the options notification to
|
||||
* display
|
||||
* @returns {Notification} the provided notification decorated with
|
||||
* functions to dismiss or minimize
|
||||
*/
|
||||
alert(message) {
|
||||
let notificationModel = typeof message === "string" ? {title: message} : message;
|
||||
notificationModel.severity = "alert";
|
||||
return this.notify(notificationModel);
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience method for error notifications. Notifications
|
||||
* created via this method will will have severity of "error" enforced
|
||||
* @param {NotificationModel | string} message either a string for
|
||||
* the title of the error message with default options, or a
|
||||
* {@link NotificationModel} defining the options of the notification to
|
||||
* display
|
||||
* @returns {Notification} the provided notification decorated with
|
||||
* functions to dismiss or minimize
|
||||
*/
|
||||
error(message) {
|
||||
let notificationModel = typeof message === "string" ? {title: message} : message;
|
||||
notificationModel.severity = "error";
|
||||
return this.notify(notificationModel);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
setHighestSeverity() {
|
||||
let severity = {
|
||||
"info": 1,
|
||||
"alert": 2,
|
||||
"error": 3
|
||||
};
|
||||
this.highest.severity = this.notifications.reduce((previous, notification) => {
|
||||
if (severity[notification.model.severity] > severity[previous]) {
|
||||
return notification.model.severity;
|
||||
} else {
|
||||
return previous;
|
||||
}
|
||||
}, "info");
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the user of an event. If there is a banner notification
|
||||
* already active, then it will be dismissed or minimized automatically,
|
||||
* and the provided notification displayed in its place.
|
||||
*
|
||||
* @param {NotificationModel} notificationModel The notification to
|
||||
* display
|
||||
* @returns {Notification} the provided notification decorated with
|
||||
* functions to {@link Notification#dismiss} or {@link Notification#minimize}
|
||||
*/
|
||||
notify(notificationModel) {
|
||||
let notification;
|
||||
let activeNotification = this.activeNotification;
|
||||
|
||||
notificationModel.severity = notificationModel.severity || "info";
|
||||
notificationModel.timestamp = moment.utc().format('YYYY-MM-DD hh:mm:ss.ms');
|
||||
|
||||
notification = new MCTNotification(notificationModel, this);
|
||||
|
||||
this.notifications.push(notification);
|
||||
this.setHighestSeverity();
|
||||
|
||||
/*
|
||||
Check if there is already an active (ie. visible) notification
|
||||
*/
|
||||
if (!this.activeNotification) {
|
||||
this.setActiveNotification(notification);
|
||||
} else if (!this.activeTimeout) {
|
||||
/*
|
||||
If there is already an active notification, time it out. If it's
|
||||
already got a timeout in progress (either because it has had
|
||||
timeout forced because of a queue of messages, or it had an
|
||||
autodismiss specified), leave it to run. Otherwise force a
|
||||
timeout.
|
||||
|
||||
This notification has been added to queue and will be
|
||||
serviced as soon as possible.
|
||||
*/
|
||||
this.activeTimeout = setTimeout(() => {
|
||||
this.dismissOrMinimize(activeNotification);
|
||||
}, DEFAULT_AUTO_DISMISS_TIMEOUT);
|
||||
}
|
||||
|
||||
return notification;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used internally by the NotificationService
|
||||
* @private
|
||||
*/
|
||||
setActiveNotification(notification) {
|
||||
let shouldAutoDismiss;
|
||||
this.activeNotification = notification;
|
||||
|
||||
if (!notification) {
|
||||
delete this.activeTimeout;
|
||||
return;
|
||||
}
|
||||
this.emit('notification', notification);
|
||||
|
||||
if (notification.model.severity === "info") {
|
||||
shouldAutoDismiss = true;
|
||||
} else {
|
||||
shouldAutoDismiss = notification.model.autoDismiss;
|
||||
}
|
||||
|
||||
if (shouldAutoDismiss || this.selectNextNotification()) {
|
||||
this.activeTimeout = setTimeout(() => {
|
||||
this.dismissOrMinimize(notification);
|
||||
}, DEFAULT_AUTO_DISMISS_TIMEOUT);
|
||||
} else {
|
||||
delete this.activeTimeout;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used internally by the NotificationService
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
selectNextNotification() {
|
||||
let notification;
|
||||
let i = 0;
|
||||
|
||||
/*
|
||||
Loop through the notifications queue and find the first one that
|
||||
has not already been minimized (manually or otherwise).
|
||||
*/
|
||||
for (; i < this.notifications.length; i++) {
|
||||
notification = this.notifications[i];
|
||||
|
||||
if (!notification.model.minimized &&
|
||||
notification !== this.activeNotification) {
|
||||
return notification;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -56,7 +56,6 @@ define([
|
||||
'../platform/features/clock/bundle',
|
||||
'../platform/features/fixed/bundle',
|
||||
'../platform/features/imagery/bundle',
|
||||
'../platform/features/layout/bundle',
|
||||
'../platform/features/my-items/bundle',
|
||||
'../platform/features/pages/bundle',
|
||||
'../platform/features/hyperlink/bundle',
|
||||
@ -99,7 +98,6 @@ define([
|
||||
'platform/features/clock',
|
||||
'platform/features/fixed',
|
||||
'platform/features/imagery',
|
||||
'platform/features/layout',
|
||||
'platform/features/pages',
|
||||
'platform/features/hyperlink',
|
||||
'platform/features/timeline',
|
||||
|
327
src/plugins/displayLayout/DisplayLayout.vue
Normal file
327
src/plugins/displayLayout/DisplayLayout.vue
Normal file
@ -0,0 +1,327 @@
|
||||
/*****************************************************************************
|
||||
* 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"
|
||||
@dragover="handleDragOver"
|
||||
@click="bypassSelection"
|
||||
@drop="handleDrop">
|
||||
<div class="l-layout__object">
|
||||
<!-- Background grid -->
|
||||
<div class="l-layout__grid-holder c-grid"
|
||||
v-if="!drilledIn">
|
||||
<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>
|
||||
<layout-frame v-for="item in frameItems"
|
||||
class="l-layout__frame"
|
||||
:key="item.id"
|
||||
:item="item"
|
||||
:gridSize="gridSize"
|
||||
@drilledIn="updateDrilledInState"
|
||||
@dragInProgress="updatePosition"
|
||||
@endDrag="endDrag">
|
||||
</layout-frame>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@import "~styles/sass-base";
|
||||
|
||||
.l-layout,
|
||||
.c-grid,
|
||||
.c-grid__x,
|
||||
.c-grid__y {
|
||||
@include abs();
|
||||
}
|
||||
|
||||
.l-layout {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&__grid-holder {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&__object {
|
||||
flex: 1 1 auto;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
&__frame {
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
|
||||
.c-grid {
|
||||
pointer-events: none;
|
||||
|
||||
&__x { @include bgTicks($colorGridLines, 'x'); }
|
||||
&__y { @include bgTicks($colorGridLines, 'y'); }
|
||||
}
|
||||
|
||||
.is-editing {
|
||||
.l-shell__main-container > .l-layout {
|
||||
// Target the top-most layout container and color its background
|
||||
background: rgba($editColor, 0.1);
|
||||
}
|
||||
|
||||
.s-selected,
|
||||
.s-selected-parent {
|
||||
.l-layout {
|
||||
// Show the layout grid for the top-most child of the current selection,
|
||||
// and hide the grid for deeper nested levels.
|
||||
[class*="__grid-holder"] {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.l-layout [class*="__grid-holder"] {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
<script>
|
||||
import LayoutFrame from './LayoutFrame.vue';
|
||||
|
||||
const DEFAULT_GRID_SIZE = [32, 32],
|
||||
DEFAULT_DIMENSIONS = [12, 8],
|
||||
DEFAULT_POSITION = [0, 0],
|
||||
MINIMUM_FRAME_SIZE = [320, 180],
|
||||
DEFAULT_HIDDEN_FRAME_TYPES = [
|
||||
'hyperlink'
|
||||
];
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
gridSize: [],
|
||||
frameItems: [],
|
||||
frames: [],
|
||||
frameStyles: [],
|
||||
rawPositions: {},
|
||||
initSelect: true,
|
||||
drilledIn: undefined
|
||||
}
|
||||
},
|
||||
inject: ['openmct'],
|
||||
props: ['domainObject'],
|
||||
components: {
|
||||
LayoutFrame
|
||||
},
|
||||
created: function () {
|
||||
this.newDomainObject = this.domainObject;
|
||||
this.gridSize = this.newDomainObject.layoutGrid || DEFAULT_GRID_SIZE;
|
||||
this.composition = this.openmct.composition.get(this.newDomainObject);
|
||||
let panels = (((this.newDomainObject.configuration || {}).layout || {}).panels || {});
|
||||
|
||||
if (this.composition !== undefined) {
|
||||
this.composition.load().then((composition) => {
|
||||
composition.forEach(function (domainObject) {
|
||||
this.readLayoutConfiguration(domainObject, panels);
|
||||
this.makeFrameItem(domainObject, false);
|
||||
}.bind(this));
|
||||
this.composition.on('add', this.onAddComposition);
|
||||
this.composition.on('remove', this.onRemoveComposition);
|
||||
});
|
||||
}
|
||||
|
||||
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));
|
||||
},
|
||||
methods: {
|
||||
readLayoutConfiguration(domainObject, panels) {
|
||||
let id = this.openmct.objects.makeKeyString(domainObject.identifier);
|
||||
this.rawPositions[id] = {
|
||||
position: panels[id].position || DEFAULT_POSITION,
|
||||
dimensions: panels[id].dimensions || this.defaultDimensions()
|
||||
};
|
||||
this.frameStyles[id] = this.convertPosition(this.rawPositions[id]);
|
||||
this.frames[id] = panels[id].hasOwnProperty('hasFrame') ?
|
||||
panels[id].hasFrame :
|
||||
this.hasFrameByDefault(domainObject.type);
|
||||
},
|
||||
makeFrameItem(domainObject, initSelect) {
|
||||
let id = this.openmct.objects.makeKeyString(domainObject.identifier);
|
||||
this.frameItems.push({
|
||||
id: id,
|
||||
hasFrame: this.frames[id],
|
||||
domainObject,
|
||||
style: this.frameStyles[id],
|
||||
drilledIn: this.isDrilledIn(id),
|
||||
initSelect: initSelect,
|
||||
rawPosition: this.rawPositions[id]
|
||||
});
|
||||
},
|
||||
onAddComposition(domainObject) {
|
||||
let id = this.openmct.objects.makeKeyString(domainObject.identifier);
|
||||
this.rawPositions[id] = {
|
||||
position: [
|
||||
Math.floor(this.droppedObjectPosition.x / this.gridSize[0]),
|
||||
Math.floor(this.droppedObjectPosition.y / this.gridSize[1])
|
||||
],
|
||||
dimensions: this.defaultDimensions()
|
||||
};
|
||||
this.frameStyles[id] = this.convertPosition(this.rawPositions[id]);
|
||||
this.frames[id] = this.hasFrameByDefault(domainObject.type);
|
||||
|
||||
let newPanel = this.rawPositions[id];
|
||||
newPanel.hasFrame = this.frames[id];
|
||||
this.mutate("configuration.layout.panels[" + id + "]", newPanel);
|
||||
this.makeFrameItem(domainObject, true);
|
||||
},
|
||||
onRemoveComposition(identifier) {
|
||||
// TODO: remove the object from frameItems
|
||||
},
|
||||
defaultDimensions() {
|
||||
let gridSize = this.gridSize;
|
||||
return MINIMUM_FRAME_SIZE.map(function (min, i) {
|
||||
return Math.max(
|
||||
Math.ceil(min / gridSize[i]),
|
||||
DEFAULT_DIMENSIONS[i]
|
||||
);
|
||||
});
|
||||
},
|
||||
convertPosition(raw) {
|
||||
return {
|
||||
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'
|
||||
};
|
||||
},
|
||||
/**
|
||||
* Checks if the frame should be hidden or not.
|
||||
*
|
||||
* @param type the domain object type
|
||||
* @return {boolean} true if the object should have
|
||||
* frame by default, false, otherwise
|
||||
*/
|
||||
hasFrameByDefault(type) {
|
||||
return DEFAULT_HIDDEN_FRAME_TYPES.indexOf(type) === -1;
|
||||
},
|
||||
setSelection(selection) {
|
||||
if (selection.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.updateDrilledInState();
|
||||
},
|
||||
updateDrilledInState(id) {
|
||||
this.drilledIn = id;
|
||||
this.frameItems.forEach(function (item) {
|
||||
item.drilledIn = item.id === id;
|
||||
});
|
||||
},
|
||||
isDrilledIn(id) {
|
||||
return this.drilledIn === id;
|
||||
},
|
||||
updatePosition(id, newPosition) {
|
||||
let newStyle = this.convertPosition(newPosition);
|
||||
this.frameStyles[id] = newStyle;
|
||||
this.rawPositions[id] = newPosition;
|
||||
this.frameItems.forEach(function (item) {
|
||||
if (item.id === id) {
|
||||
item.style = newStyle;
|
||||
item.rawPosition = newPosition;
|
||||
}
|
||||
});
|
||||
},
|
||||
bypassSelection($event) {
|
||||
if (this.dragInProgress) {
|
||||
if ($event) {
|
||||
$event.stopImmediatePropagation();
|
||||
}
|
||||
return;
|
||||
}
|
||||
},
|
||||
endDrag(id) {
|
||||
this.dragInProgress = true;
|
||||
setTimeout(function () {
|
||||
this.dragInProgress = false;
|
||||
}.bind(this), 0);
|
||||
|
||||
let path = "configuration.layout.panels[" + id + "]";
|
||||
this.mutate(path + ".dimensions", this.rawPositions[id].dimensions);
|
||||
this.mutate(path + ".position", this.rawPositions[id].position);
|
||||
},
|
||||
mutate(path, value) {
|
||||
this.openmct.objects.mutate(this.newDomainObject, path, value);
|
||||
},
|
||||
handleDrop($event) {
|
||||
$event.preventDefault();
|
||||
|
||||
let child = JSON.parse($event.dataTransfer.getData('domainObject'));
|
||||
let duplicates = [];
|
||||
let composition = this.newDomainObject.composition;
|
||||
composition.forEach((object) => {
|
||||
if (this.openmct.objects.makeKeyString(JSON.parse(JSON.stringify(object))) ===
|
||||
this.openmct.objects.makeKeyString(child.identifier)) {
|
||||
duplicates.push(object);
|
||||
}
|
||||
});
|
||||
|
||||
// Disallow adding a duplicate object to the composition
|
||||
if (duplicates.length !== 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let elementRect = this.$el.getBoundingClientRect();
|
||||
this.droppedObjectPosition = {
|
||||
x: $event.pageX - elementRect.left,
|
||||
y: $event.pageY - elementRect.top
|
||||
}
|
||||
// TODO: use the composition API to add child once the default composition
|
||||
// provider supports it instead of mutating the composition directly.
|
||||
// this.composition.add(child).
|
||||
composition.push(child.identifier);
|
||||
this.mutate('composition', composition);
|
||||
},
|
||||
handleDragOver($event){
|
||||
$event.preventDefault();
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.openmct.selection.on('change', this.setSelection);
|
||||
},
|
||||
destroyed: function () {
|
||||
this.composition.off('add', this.onAddComposition);
|
||||
this.composition.off('remove', this.onRemoveComposition);
|
||||
this.openmct.off('change', this.selection);
|
||||
this.unlisten();
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
41
src/plugins/displayLayout/DisplayLayoutType.js
Normal file
41
src/plugins/displayLayout/DisplayLayoutType.js
Normal file
@ -0,0 +1,41 @@
|
||||
/*****************************************************************************
|
||||
* 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 () {
|
||||
function DisplayLayoutType() {
|
||||
return {
|
||||
name: "Display Layout",
|
||||
creatable: true,
|
||||
cssClass: 'icon-layout',
|
||||
initialize(domainObject) {
|
||||
domainObject.composition = [];
|
||||
domainObject.configuration = {
|
||||
layout: {
|
||||
panels: {}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return DisplayLayoutType;
|
||||
});
|
115
src/plugins/displayLayout/LayoutDrag.js
Normal file
115
src/plugins/displayLayout/LayoutDrag.js
Normal file
@ -0,0 +1,115 @@
|
||||
/*****************************************************************************
|
||||
* 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 () {
|
||||
|
||||
/**
|
||||
* Handles drag interactions on frames in layouts. This will
|
||||
* provides new positions/dimensions for frames based on
|
||||
* relative pixel positions provided; these will take into account
|
||||
* the grid size (in a snap-to sense) and will enforce some minimums
|
||||
* on both position and dimensions.
|
||||
*
|
||||
* The provided position and dimensions factors will determine
|
||||
* whether this is a move or a resize, and what type of resize it
|
||||
* will be. For instance, a position factor of [1, 1]
|
||||
* will move a frame along with the mouse as the drag
|
||||
* proceeds, while a dimension factor of [0, 0] will leave
|
||||
* dimensions unchanged. Combining these in different
|
||||
* ways results in different handles; a position factor of
|
||||
* [1, 0] and a dimensions factor of [-1, 0] will implement
|
||||
* a left-edge resize, as the horizontal position will move
|
||||
* with the mouse while the horizontal dimensions shrink in
|
||||
* kind (and vertical properties remain unmodified.)
|
||||
*
|
||||
* @param {object} rawPosition the initial position/dimensions
|
||||
* of the frame being interacted with
|
||||
* @param {number[]} posFactor the position factor
|
||||
* @param {number[]} dimFactor the dimensions factor
|
||||
* @param {number[]} the size of each grid element, in pixels
|
||||
* @constructor
|
||||
* @memberof platform/features/layout
|
||||
*/
|
||||
function LayoutDrag(rawPosition, posFactor, dimFactor, gridSize) {
|
||||
this.rawPosition = rawPosition;
|
||||
this.posFactor = posFactor;
|
||||
this.dimFactor = dimFactor;
|
||||
this.gridSize = gridSize;
|
||||
}
|
||||
|
||||
// Convert a delta from pixel coordinates to grid coordinates,
|
||||
// rounding to whole-number grid coordinates.
|
||||
function toGridDelta(gridSize, pixelDelta) {
|
||||
return pixelDelta.map(function (v, i) {
|
||||
return Math.round(v / gridSize[i]);
|
||||
});
|
||||
}
|
||||
|
||||
// Utility function to perform element-by-element multiplication
|
||||
function multiply(array, factors) {
|
||||
return array.map(function (v, i) {
|
||||
return v * factors[i];
|
||||
});
|
||||
}
|
||||
|
||||
// Utility function to perform element-by-element addition
|
||||
function add(array, other) {
|
||||
return array.map(function (v, i) {
|
||||
return v + other[i];
|
||||
});
|
||||
}
|
||||
|
||||
// Utility function to perform element-by-element max-choosing
|
||||
function max(array, other) {
|
||||
return array.map(function (v, i) {
|
||||
return Math.max(v, other[i]);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a new position object in grid coordinates, with
|
||||
* position and dimensions both offset appropriately
|
||||
* according to the factors supplied in the constructor.
|
||||
* @param {number[]} pixelDelta the offset from the
|
||||
* original position, in pixels
|
||||
*/
|
||||
LayoutDrag.prototype.getAdjustedPosition = function (pixelDelta) {
|
||||
var gridDelta = toGridDelta(this.gridSize, pixelDelta);
|
||||
return {
|
||||
position: max(add(
|
||||
this.rawPosition.position,
|
||||
multiply(gridDelta, this.posFactor)
|
||||
), [0, 0]),
|
||||
dimensions: max(add(
|
||||
this.rawPosition.dimensions,
|
||||
multiply(gridDelta, this.dimFactor)
|
||||
), [1, 1])
|
||||
};
|
||||
};
|
||||
|
||||
return LayoutDrag;
|
||||
|
||||
}
|
||||
);
|
338
src/plugins/displayLayout/LayoutFrame.vue
Normal file
338
src/plugins/displayLayout/LayoutFrame.vue
Normal file
@ -0,0 +1,338 @@
|
||||
/*****************************************************************************
|
||||
* 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 is-selectable is-moveable"
|
||||
:style="item.style"
|
||||
:class="classObject"
|
||||
@dblclick="drill(item.id, $event)">
|
||||
<div class="c-frame__header">
|
||||
<div class="c-frame__header__start">
|
||||
<div class="c-frame__name icon-object">{{ item.domainObject.name }}</div>
|
||||
<div class="c-frame__context-actions c-disclosure-button"></div>
|
||||
</div>
|
||||
<div class="c-frame__header__end">
|
||||
<div class="c-button icon-expand local-controls--hidden"></div>
|
||||
</div>
|
||||
</div>
|
||||
<object-view class="c-frame__object-view"
|
||||
:object="item.domainObject"></object-view>
|
||||
|
||||
<!-- 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 --nw"
|
||||
@mousedown="startDrag([1,1], [-1,-1], $event)"></div>
|
||||
<div class="c-frame-edit__handle --ne"
|
||||
@mousedown="startDrag([0,1], [1,-1], $event)"></div>
|
||||
<div class="c-frame-edit__handle --sw"
|
||||
@mousedown="startDrag([1,0], [-1,1], $event)"></div>
|
||||
<div class="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-width: 1px;
|
||||
border-color: transparent;
|
||||
|
||||
/*************************** HEADER */
|
||||
&__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 0 0 auto;
|
||||
margin-bottom: $interiorMargin;
|
||||
|
||||
> [class*="__"] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
> * + * {
|
||||
margin-left: $interiorMargin;
|
||||
}
|
||||
|
||||
[class*="__start"] {
|
||||
flex: 1 1 auto;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
[class*="__end"] {
|
||||
//justify-content: flex-end;
|
||||
flex: 0 0 auto;
|
||||
|
||||
[class*="button"] {
|
||||
font-size: 0.7em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__name {
|
||||
@include ellipsize();
|
||||
flex: 0 1 auto;
|
||||
font-size: 1.2em;
|
||||
|
||||
&:before {
|
||||
// Object type icon
|
||||
flex: 0 0 auto;
|
||||
margin-right: $interiorMarginSm;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************** OBJECT VIEW */
|
||||
&__object-view {
|
||||
flex: 1 1 auto;
|
||||
overflow: auto;
|
||||
|
||||
.c-object-view {
|
||||
.u-fills-container {
|
||||
// Expand component types that fill a container
|
||||
@include abs();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************** NO-FRAME */
|
||||
&.no-frame {
|
||||
> [class*="__header"] {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.no-frame) {
|
||||
background: $colorBodyBg;
|
||||
border: 1px solid $colorInteriorBorder;
|
||||
padding: $interiorMargin;
|
||||
}
|
||||
|
||||
/*************************** SELECTION */
|
||||
&.is-selectable {
|
||||
&:hover {
|
||||
box-shadow: $browseShdwSelectableHov;
|
||||
}
|
||||
}
|
||||
|
||||
&.s-selected, // LEGACY
|
||||
&.is-selected {
|
||||
border: $browseBorderSelected;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************** EDITING */
|
||||
.is-editing {
|
||||
.c-frame {
|
||||
&:not(.is-drilled-in).is-selectable {
|
||||
border: $editBorderSelectable;
|
||||
|
||||
&:hover {
|
||||
border: $editBorderSelectableHov;
|
||||
}
|
||||
|
||||
&.s-selected,
|
||||
&.is-selected {
|
||||
border: $editBorderSelected;
|
||||
|
||||
> .c-frame-edit {
|
||||
display: block; // Show the editing rect and handles
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.is-drilled-in {
|
||||
border: $editBorderDrilledIn;
|
||||
}
|
||||
|
||||
.u-links {
|
||||
// Applied in markup to objects that provide links. Disable while editing.
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.c-frame-edit {
|
||||
// The editing rect and handles
|
||||
$z: 10;
|
||||
|
||||
@include abs();
|
||||
box-shadow: rgba($editColor, 0.5) 0 0 10px;
|
||||
display: none;
|
||||
|
||||
&__move {
|
||||
@include abs();
|
||||
cursor: move;
|
||||
z-index: $z;
|
||||
}
|
||||
|
||||
&__handle {
|
||||
$d: 8px;
|
||||
$o: floor($d * -0.5);
|
||||
background: rgba($editColor, 0.3);
|
||||
border: 1px solid $editColor;
|
||||
position: absolute;
|
||||
width: $d; height: $d;
|
||||
top: auto; right: auto; bottom: auto; left: auto;
|
||||
z-index: $z + 1;
|
||||
|
||||
&:before {
|
||||
// Extended hit area
|
||||
$m: -5px;
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: $m; right: $m; bottom: $m; left: $m;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: $editColor;
|
||||
}
|
||||
|
||||
&.--nw {
|
||||
cursor: nw-resize;
|
||||
left: $o; top: $o;
|
||||
}
|
||||
|
||||
&.--ne {
|
||||
cursor: ne-resize;
|
||||
right: $o; top: $o;
|
||||
}
|
||||
|
||||
&.--se {
|
||||
cursor: se-resize;
|
||||
right: $o; bottom: $o;
|
||||
}
|
||||
|
||||
&.--sw {
|
||||
cursor: sw-resize;
|
||||
left: $o; bottom: $o;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
<script>
|
||||
import ObjectView from '../../ui/components/layout/ObjectView.vue'
|
||||
import LayoutDrag from './LayoutDrag'
|
||||
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
props: {
|
||||
item: Object,
|
||||
gridSize: Array
|
||||
},
|
||||
components: {
|
||||
ObjectView
|
||||
},
|
||||
computed: {
|
||||
classObject: function () {
|
||||
return {
|
||||
'is-drilled-in': this.item.drilledIn,
|
||||
'no-frame': !this.item.hasFrame
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
drill(id, $event) {
|
||||
if ($event) {
|
||||
$event.stopPropagation();
|
||||
}
|
||||
|
||||
if (!this.isBeingEdited(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);
|
||||
},
|
||||
isBeingEdited(object) {
|
||||
// TODO: add logic when inEditContext() is implemented in Vue.
|
||||
return true;
|
||||
},
|
||||
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.rawPosition,
|
||||
posFactor,
|
||||
dimFactor,
|
||||
this.gridSize
|
||||
);
|
||||
event.preventDefault();
|
||||
},
|
||||
continueDrag(event) {
|
||||
event.preventDefault();
|
||||
this.updatePosition(event);
|
||||
|
||||
if (this.activeDrag) {
|
||||
this.$emit('dragInProgress', this.item.id, 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.id);
|
||||
this.initialPosition = undefined;
|
||||
event.preventDefault();
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.removeSelectable = this.openmct.selection.selectable(
|
||||
this.$el,
|
||||
{
|
||||
item: this.item.domainObject
|
||||
},
|
||||
this.item.initSelect
|
||||
);
|
||||
},
|
||||
destroyed() {
|
||||
this.removeSelectable();
|
||||
}
|
||||
}
|
||||
</script>
|
67
src/plugins/displayLayout/plugin.js
Normal file
67
src/plugins/displayLayout/plugin.js
Normal file
@ -0,0 +1,67 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
import Layout from './DisplayLayout.vue'
|
||||
import Vue from 'vue'
|
||||
import objectUtils from '../../api/objects/object-utils.js'
|
||||
import DisplayLayoutType from './DisplayLayoutType.js'
|
||||
|
||||
export default function () {
|
||||
return function (openmct) {
|
||||
openmct.objectViews.addProvider({
|
||||
key: 'layout.view',
|
||||
canView: function (domainObject) {
|
||||
return domainObject.type === 'layout';
|
||||
},
|
||||
view: function (domainObject) {
|
||||
let component;
|
||||
return {
|
||||
show(container) {
|
||||
component = new Vue({
|
||||
components: {
|
||||
Layout
|
||||
},
|
||||
template: '<layout :domain-object="domainObject"></layout>',
|
||||
provide: {
|
||||
openmct,
|
||||
objectUtils
|
||||
},
|
||||
el: container,
|
||||
data () {
|
||||
return {
|
||||
domainObject: domainObject
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
destroy() {
|
||||
component.$destroy();
|
||||
}
|
||||
};
|
||||
},
|
||||
priority() {
|
||||
return 100;
|
||||
}
|
||||
});
|
||||
openmct.types.addType('layout', DisplayLayoutType());
|
||||
}
|
||||
}
|
@ -14,9 +14,9 @@
|
||||
:title="item.model.name">{{item.model.name}}</div>
|
||||
<div class="c-grid-item__metadata"
|
||||
:title="item.type.name">
|
||||
<span>{{item.type.name}}</span>
|
||||
<span v-if="item.model.composition !== undefined">
|
||||
- {{item.model.composition.length}} item<span v-if="item.model.composition.length !== 1">s</span>
|
||||
<span class="c-grid-item__metadata__type">{{item.type.name}}</span>
|
||||
<span class="c-grid-item__metadata__item-count" v-if="item.model.composition !== undefined">
|
||||
{{item.model.composition.length}} item<span v-if="item.model.composition.length !== 1">s</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -45,8 +45,8 @@
|
||||
body.desktop & {
|
||||
flex-flow: row wrap;
|
||||
&__item {
|
||||
height: $ueBrowseGridItemLg;
|
||||
width: $ueBrowseGridItemLg;
|
||||
height: $gridItemDesk;
|
||||
width: $gridItemDesk;
|
||||
margin: 0 $interiorMargin $interiorMargin 0;
|
||||
}
|
||||
}
|
||||
@ -62,8 +62,8 @@
|
||||
|
||||
&__type-icon {
|
||||
filter: $colorKeyFilter;
|
||||
flex: 0 0 32px;
|
||||
font-size: 2em; // Drives the size of the alias indicator when present
|
||||
flex: 0 0 $gridItemMobile;
|
||||
font-size: floor($gridItemMobile / 2);
|
||||
margin-right: $interiorMarginLg;
|
||||
}
|
||||
|
||||
@ -84,13 +84,22 @@
|
||||
&__name {
|
||||
@include ellipsize();
|
||||
color: $colorItemFg;
|
||||
font-size: 1.3em;
|
||||
font-size: 1.2em;
|
||||
font-weight: 400;
|
||||
margin-bottom: $interiorMarginSm;
|
||||
}
|
||||
|
||||
&__metadata {
|
||||
color: $colorItemFgDetails;
|
||||
font-size: 0.9em;
|
||||
|
||||
body.mobile & {
|
||||
[class*='__item-count'] {
|
||||
&:before {
|
||||
content: ' - ';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__controls {
|
||||
@ -136,8 +145,8 @@
|
||||
|
||||
&__type-icon {
|
||||
flex: 1 1 auto;
|
||||
font-size: 6em; // Drives the size of the alias indicator when present
|
||||
margin: $interiorMargin 22.5%;
|
||||
font-size: floor($gridItemDesk / 3);
|
||||
margin: $interiorMargin 22.5% $interiorMargin * 3 22.5%;
|
||||
order: 2;
|
||||
transform: scale(0.9);
|
||||
transform-origin: center;
|
||||
@ -149,6 +158,20 @@
|
||||
justify-content: flex-end;
|
||||
order: 3;
|
||||
}
|
||||
|
||||
&__metadata {
|
||||
display: flex;
|
||||
|
||||
&__type {
|
||||
flex: 1 1 auto;
|
||||
@include ellipsize();
|
||||
}
|
||||
|
||||
&__item-count {
|
||||
opacity: 0.7;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -8,8 +8,7 @@
|
||||
<span>{{formatTime(entry.createdOn, 'HH:mm:ss')}}</span>
|
||||
</div>
|
||||
<div class="c-ne__content">
|
||||
<!-- TODO: fix styling for c-input-inline when SCSS is merged and remove s-input-inline class here -->
|
||||
<div class="c-ne__text c-input-inline s-input-inline"
|
||||
<div class="c-ne__text c-input-inline"
|
||||
contenteditable="true"
|
||||
ref="contenteditable"
|
||||
v-on:blur="textBlur($event, entry.id)"
|
||||
|
29
src/plugins/notebook/res/templates/snapshotTemplate.html
Normal file
29
src/plugins/notebook/res/templates/snapshotTemplate.html
Normal file
@ -0,0 +1,29 @@
|
||||
<div class="u-contents">
|
||||
|
||||
<div class="t-snapshot abs l-view-header">
|
||||
<div class="abs object-browse-bar l-flex-row">
|
||||
<div class="left flex-elem l-flex-row grows">
|
||||
<div class="object-header flex-elem l-flex-row grows">
|
||||
<div class="type-icon flex-elem embed-icon holder" v-bind:class="embed.cssClass"></div>
|
||||
<div class="title-label flex-elem holder flex-can-shrink">{{embed.name}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="btn-bar right l-flex-row flex-elem flex-justify-end flex-fixed">
|
||||
<div class="flex-elem holder flex-can-shrink s-snapshot-datetime">
|
||||
SNAPSHOT {{formatTime(embed.createdOn, 'YYYY-MM-DD HH:mm:ss')}}
|
||||
</div>
|
||||
<a class="s-button icon-pencil" title="Annotate">
|
||||
<span class="title-label">Annotate</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="abs object-holder t-image-holder s-image-holder">
|
||||
<div
|
||||
class="image-main s-image-main"
|
||||
v-bind:style="{ backgroundImage: 'url(' + embed.snapshot.src + ')' }">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -24,11 +24,15 @@ define([
|
||||
'moment',
|
||||
'zepto',
|
||||
'../utils/SnapshotOverlay',
|
||||
'../../res/templates/snapshotTemplate.html',
|
||||
'vue'
|
||||
],
|
||||
function (
|
||||
Moment,
|
||||
$,
|
||||
SnapshotOverlay
|
||||
SnapshotOverlay,
|
||||
SnapshotTemplate,
|
||||
Vue
|
||||
) {
|
||||
function EmbedController (openmct, domainObject) {
|
||||
this.openmct = openmct;
|
||||
@ -53,11 +57,24 @@ function (
|
||||
};
|
||||
|
||||
EmbedController.prototype.openSnapshot = function () {
|
||||
if (!this.snapshotOverlay) {
|
||||
this.snapShotOverlay = new SnapshotOverlay(this.embed, this.formatTime);
|
||||
} else {
|
||||
this.snapShotOverlay = undefined;
|
||||
var self = this,
|
||||
snapshot = new Vue({
|
||||
template: SnapshotTemplate,
|
||||
data: function () {
|
||||
return {
|
||||
embed: self.embed
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
formatTime: self.formatTime
|
||||
}
|
||||
});
|
||||
|
||||
function onDestroyCallback() {
|
||||
snapshot.$destroy(true);
|
||||
}
|
||||
|
||||
this.openmct.OverlayService.show(snapshot.$mount().$el, {onDestroy: onDestroyCallback, cssClass: 'l-large-view'});
|
||||
};
|
||||
|
||||
EmbedController.prototype.formatTime = function (unixTime, timeFormat) {
|
||||
|
@ -60,6 +60,7 @@ function (
|
||||
this.container = container;
|
||||
|
||||
var notebookEmbed = {
|
||||
inject:['openmct'],
|
||||
props:['embed', 'entry'],
|
||||
template: EmbedTemplate,
|
||||
data: embedController.exposedData,
|
||||
@ -80,6 +81,7 @@ function (
|
||||
|
||||
var notebookVue = Vue.extend({
|
||||
template: NotebookTemplate,
|
||||
provide: {openmct: self.openmct},
|
||||
components: {
|
||||
'notebook-entry': entryComponent,
|
||||
'search': search.default
|
||||
|
@ -35,6 +35,7 @@ define([
|
||||
'./telemetryTable/plugin',
|
||||
'./staticRootPlugin/plugin',
|
||||
'./notebook/plugin',
|
||||
'./displayLayout/plugin',
|
||||
'./folderView/plugin'
|
||||
], function (
|
||||
_,
|
||||
@ -51,6 +52,7 @@ define([
|
||||
TelemetryTablePlugin,
|
||||
StaticRootPlugin,
|
||||
Notebook,
|
||||
DisplayLayoutPlugin,
|
||||
FolderView
|
||||
) {
|
||||
var bundleMap = {
|
||||
@ -161,6 +163,7 @@ define([
|
||||
plugins.TelemetryMean = TelemetryMean;
|
||||
plugins.URLIndicator = URLIndicatorPlugin;
|
||||
plugins.Notebook = Notebook;
|
||||
plugins.DisplayLayout = DisplayLayoutPlugin.default;
|
||||
plugins.FolderView = FolderView;
|
||||
|
||||
return plugins;
|
||||
|
@ -264,7 +264,7 @@ define([
|
||||
this.applyStyle($('#widget', this.domElement), activeRule.getProperty('style'));
|
||||
$('#widget', this.domElement).prop('title', activeRule.getProperty('message'));
|
||||
$('#widgetLabel', this.domElement).html(activeRule.getProperty('label'));
|
||||
$('#widgetLabel', this.domElement).removeClass().addClass('label widget-label ' + activeRule.getProperty('icon'));
|
||||
$('#widgetLabel', this.domElement).removeClass().addClass('label widget-label c-summary-widget__label ' + activeRule.getProperty('icon'));
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -18,7 +18,7 @@ define([
|
||||
this.widget.title = datum.message;
|
||||
this.label.title = datum.message;
|
||||
this.label.innerHTML = datum.ruleLabel;
|
||||
this.label.className = 'label widget-label ' + datum.icon;
|
||||
this.label.className = 'label widget-label c-summary-widget__label ' + datum.icon;
|
||||
};
|
||||
|
||||
SummaryWidgetView.prototype.render = function () {
|
||||
|
@ -1,5 +1,5 @@
|
||||
<div class="w-summary-widget s-status-no-data">
|
||||
<a class="t-summary-widget l-summary-widget s-summary-widget labeled">
|
||||
<span class="label widget-label">Loading...</span>
|
||||
<div class="w-summary-widget s-status-no-data c-widget-wrapper u-contents">
|
||||
<a class="t-summary-widget c-button c-summary-widget u-links u-fills-container">
|
||||
<span class="label widget-label c-summary-widget__label">Loading...</span>
|
||||
</a>
|
||||
</div>
|
||||
|
@ -33,30 +33,6 @@ define([
|
||||
) {
|
||||
|
||||
function TableConfigurationViewProvider(openmct) {
|
||||
let instantiateService;
|
||||
|
||||
function isBeingEdited(object) {
|
||||
let oldStyleObject = getOldStyleObject(object);
|
||||
|
||||
return oldStyleObject.hasCapability('editor') &&
|
||||
oldStyleObject.getCapability('editor').inEditContext();
|
||||
}
|
||||
|
||||
function getOldStyleObject(object) {
|
||||
let oldFormatModel = objectUtils.toOldFormat(object);
|
||||
let oldFormatId = objectUtils.makeKeyString(object.identifier);
|
||||
|
||||
return instantiate(oldFormatModel, oldFormatId);
|
||||
}
|
||||
|
||||
function instantiate(model, id) {
|
||||
if (!instantiateService) {
|
||||
instantiateService = openmct.$injector.get('instantiate');
|
||||
}
|
||||
return instantiateService(model, id);
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
key: 'table-configuration',
|
||||
name: 'Telemetry Table Configuration',
|
||||
@ -65,8 +41,7 @@ define([
|
||||
return false;
|
||||
}
|
||||
let object = selection[0].context.item;
|
||||
return object.type === 'table' &&
|
||||
isBeingEdited(object);
|
||||
return object.type === 'table';
|
||||
},
|
||||
view: function (selection) {
|
||||
let component;
|
||||
@ -86,7 +61,7 @@ define([
|
||||
el: element
|
||||
});
|
||||
},
|
||||
destroy: function (element) {
|
||||
destroy: function () {
|
||||
component.$destroy();
|
||||
component = undefined;
|
||||
}
|
||||
|
@ -23,10 +23,10 @@
|
||||
define([
|
||||
'lodash',
|
||||
'EventEmitter',
|
||||
'./TelemetryTableColumn',
|
||||
'./TelemetryTableColumn'
|
||||
], function (_, EventEmitter, TelemetryTableColumn) {
|
||||
|
||||
class TelemetryTableConfiguration extends EventEmitter{
|
||||
class TelemetryTableConfiguration extends EventEmitter {
|
||||
constructor(domainObject, openmct) {
|
||||
super();
|
||||
|
||||
@ -37,6 +37,8 @@ define([
|
||||
this.addColumnsForObject = this.addColumnsForObject.bind(this);
|
||||
this.removeColumnsForObject = this.removeColumnsForObject.bind(this);
|
||||
this.objectMutated = this.objectMutated.bind(this);
|
||||
//Make copy of configuration, otherwise change detection is impossible if shared instance is being modified.
|
||||
this.oldConfiguration = JSON.parse(JSON.stringify(this.getConfiguration()));
|
||||
|
||||
this.unlistenFromMutation = openmct.objects.observe(domainObject, '*', this.objectMutated);
|
||||
}
|
||||
@ -56,11 +58,11 @@ define([
|
||||
* @param {*} object
|
||||
*/
|
||||
objectMutated(object) {
|
||||
let oldConfiguration = this.domainObject.configuration;
|
||||
|
||||
//Synchronize domain object reference. Duplicate object otherwise change detection becomes impossible.
|
||||
this.domainObject = JSON.parse(JSON.stringify(object));
|
||||
if (!_.eq(object.configuration, oldConfiguration)){
|
||||
this.domainObject = object;
|
||||
if (!_.eq(object.configuration, this.oldConfiguration)) {
|
||||
//Make copy of configuration, otherwise change detection is impossible if shared instance is being modified.
|
||||
this.oldConfiguration = JSON.parse(JSON.stringify(this.getConfiguration()));
|
||||
this.emit('change', object.configuration);
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,12 @@
|
||||
<template>
|
||||
<div class="grid-properties">
|
||||
<!--form class="form" -->
|
||||
<ul class="l-inspector-part">
|
||||
<h2>Table Columns</h2>
|
||||
<li class="grid-row" v-for="(title, key) in headers">
|
||||
<div class="grid-cell label" title="Show or Hide Column"><label :for="key + 'ColumnControl'">{{title}}</label></div>
|
||||
<div class="grid-cell value"><input type="checkbox" :id="key + 'ColumnControl'" :checked="configuration.hiddenColumns[key] !== true" @change="toggleColumn(key)"></div>
|
||||
<div class="c-properties" v-if="isEditing">
|
||||
<div class="c-properties__header">Table Columns</div>
|
||||
<ul class="c-properties__section">
|
||||
<li class="c-properties__row" v-for="(title, key) in headers">
|
||||
<div class="c-properties__label" title="Show or Hide Column"><label :for="key + 'ColumnControl'">{{title}}</label></div>
|
||||
<div class="c-properties__value"><input type="checkbox" :id="key + 'ColumnControl'" :checked="configuration.hiddenColumns[key] !== true" @change="toggleColumn(key)"></div>
|
||||
</li>
|
||||
</ul>
|
||||
<!--/form -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -21,6 +19,7 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
headers: {},
|
||||
isEditing: this.openmct.editor.isEditing(),
|
||||
configuration: this.tableConfiguration.getConfiguration()
|
||||
}
|
||||
},
|
||||
@ -41,11 +40,14 @@ export default {
|
||||
removeObject(objectIdentifier) {
|
||||
this.tableConfiguration.removeColumnsForObject(objectIdentifier, true);
|
||||
this.updateHeaders(this.tableConfiguration.getAllHeaders());
|
||||
},
|
||||
toggleEdit(isEditing) {
|
||||
this.isEditing = isEditing;
|
||||
}
|
||||
|
||||
},
|
||||
mounted() {
|
||||
this.unlisteners = [];
|
||||
this.openmct.editor.on('isEditing', this.toggleEdit);
|
||||
let compositionCollection = this.openmct.composition.get(this.tableConfiguration.domainObject);
|
||||
|
||||
compositionCollection.load()
|
||||
@ -62,6 +64,7 @@ export default {
|
||||
},
|
||||
destroyed() {
|
||||
this.tableConfiguration.destroy();
|
||||
this.openmct.editor.off('isEditing', this.toggleEdit);
|
||||
this.unlisteners.forEach((unlisten) => unlisten());
|
||||
}
|
||||
}
|
||||
|
@ -27,12 +27,10 @@
|
||||
|
||||
<ConductorModeIcon class="c-conductor__mode-icon"></ConductorModeIcon>
|
||||
|
||||
<div class="c-conductor__start-input">
|
||||
<!-- Start input and controls -->
|
||||
<div class="c-ctrl-wrapper c-conductor-input c-conductor__start__fixed"
|
||||
<div class="c-ctrl-wrapper c-conductor-input c-conductor__start-fixed"
|
||||
v-if="isFixed">
|
||||
<!-- Fixed input -->
|
||||
<div class="c-conductor__start__fixed__label">Start</div>
|
||||
<!-- Fixed start -->
|
||||
<div class="c-conductor__start-fixed__label">Start</div>
|
||||
<input class="c-input--datetime"
|
||||
type="text" autocorrect="off" spellcheck="false"
|
||||
ref="startDate"
|
||||
@ -44,9 +42,9 @@
|
||||
@date-selected="startDateSelected"></date-picker>
|
||||
</div>
|
||||
|
||||
<div class="c-ctrl-wrapper c-conductor-input c-conductor__start__delta"
|
||||
<div class="c-ctrl-wrapper c-conductor-input c-conductor__start-delta"
|
||||
v-if="!isFixed">
|
||||
<!-- RT input -->
|
||||
<!-- RT start -->
|
||||
<div class="c-direction-indicator icon-minus"></div>
|
||||
<input class="c-input--hrs-min-sec"
|
||||
type="text" autocorrect="off"
|
||||
@ -54,14 +52,12 @@
|
||||
v-model="offsets.start"
|
||||
@change="validateOffsets($event); setOffsetsFromView()">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="c-conductor__end-input">
|
||||
<!-- End input and controls -->
|
||||
<div class="c-ctrl-wrapper c-conductor-input c-conductor__end__fixed"
|
||||
v-if="isFixed">
|
||||
<!-- Fixed input -->
|
||||
<div class="c-conductor__end__fixed__label">End</div>
|
||||
<div class="c-ctrl-wrapper c-conductor-input c-conductor__end-fixed">
|
||||
<!-- Fixed end and RT 'last update' display -->
|
||||
<div class="c-conductor__end-fixed__label">
|
||||
{{ isFixed ? 'End' : 'Updated' }}
|
||||
</div>
|
||||
<input class="c-input--datetime"
|
||||
type="text" autocorrect="off" spellcheck="false"
|
||||
v-model="formattedBounds.end"
|
||||
@ -72,12 +68,13 @@
|
||||
class="c-ctrl-wrapper--menus-left"
|
||||
:default-date-time="formattedBounds.end"
|
||||
:formatter="timeFormatter"
|
||||
@date-selected="endDateSelected"></date-picker>
|
||||
@date-selected="endDateSelected"
|
||||
v-if="isFixed"></date-picker>
|
||||
</div>
|
||||
|
||||
<div class="c-ctrl-wrapper c-conductor-input c-conductor__end__delta"
|
||||
<div class="c-ctrl-wrapper c-conductor-input c-conductor__end-delta"
|
||||
v-if="!isFixed">
|
||||
<!-- RT input -->
|
||||
<!-- RT end -->
|
||||
<div class="c-direction-indicator icon-plus"></div>
|
||||
<input class="c-input--hrs-min-sec"
|
||||
type="text"
|
||||
@ -86,7 +83,6 @@
|
||||
v-model="offsets.end"
|
||||
@change="validateOffsets($event); setOffsetsFromView()">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<conductor-axis
|
||||
class="c-conductor__ticks"
|
||||
@ -112,65 +108,28 @@
|
||||
grid-row-gap: $interiorMargin;
|
||||
align-items: center;
|
||||
|
||||
|
||||
// Default: fixed mode, desktop
|
||||
grid-template-rows: 1fr 1fr;
|
||||
grid-template-columns: 20px auto 1fr auto;
|
||||
grid-template-areas:
|
||||
"tc-mode-icon tc-start tc-ticks tc-end"
|
||||
"tc-controls tc-controls tc-controls tc-controls";
|
||||
|
||||
.c-conductor__end-input {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
body.phone.portrait & {
|
||||
&.is-fixed-mode {
|
||||
grid-row-gap: $interiorMargin;
|
||||
grid-template-rows: auto auto auto;
|
||||
grid-template-columns: 20px auto;
|
||||
grid-template-areas:
|
||||
"tc-mode-icon tc-start"
|
||||
"tc-mode-icon tc-end"
|
||||
"tc-mode-icon tc-controls";
|
||||
|
||||
.c-conductor {
|
||||
&__mode-icon {
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
&__ticks,
|
||||
&__zoom {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&-input [class*='__label'] {
|
||||
// Start and end are in separate columns; make the labels line up
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
&__end-input {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__mode-icon {
|
||||
grid-area: tc-mode-icon;
|
||||
}
|
||||
|
||||
&__start-input,
|
||||
&__end-input {
|
||||
&__start-fixed,
|
||||
&__start-delta {
|
||||
grid-area: tc-start;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
&__start-input {
|
||||
grid-area: tc-start;
|
||||
}
|
||||
|
||||
&__end-input {
|
||||
&__end-fixed,
|
||||
&__end-delta {
|
||||
grid-area: tc-end;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
&__ticks {
|
||||
@ -186,12 +145,68 @@
|
||||
}
|
||||
}
|
||||
|
||||
[class*='__delta'] {
|
||||
[class*='-delta'] {
|
||||
&:before {
|
||||
content: $glyph-icon-clock;
|
||||
font-family: symbolsfont;
|
||||
}
|
||||
}
|
||||
|
||||
&.is-realtime-mode {
|
||||
grid-template-columns: 20px auto 1fr auto auto;
|
||||
grid-template-areas:
|
||||
"tc-mode-icon tc-start tc-ticks tc-updated tc-end"
|
||||
"tc-controls tc-controls tc-controls tc-controls tc-controls";
|
||||
|
||||
.c-conductor__end-fixed {
|
||||
grid-area: tc-updated;
|
||||
}
|
||||
}
|
||||
|
||||
body.phone.portrait & {
|
||||
grid-row-gap: $interiorMargin;
|
||||
grid-template-rows: auto auto auto;
|
||||
grid-template-columns: 20px auto auto;
|
||||
|
||||
&__mode-icon {
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
&__ticks,
|
||||
&__zoom {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.is-fixed-mode {
|
||||
[class*='__start-fixed'],
|
||||
[class*='__end-fixed'] {
|
||||
[class*='__label'] {
|
||||
// Start and end are in separate columns; make the labels line up
|
||||
width: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
[class*='__end-input'] {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
grid-template-areas:
|
||||
"tc-mode-icon tc-start tc-start"
|
||||
"tc-mode-icon tc-end tc-end"
|
||||
"tc-mode-icon tc-controls tc-controls";
|
||||
}
|
||||
|
||||
&.is-realtime-mode {
|
||||
grid-template-areas:
|
||||
"tc-mode-icon tc-start tc-updated"
|
||||
"tc-mode-icon tc-end tc-end"
|
||||
"tc-mode-icon tc-controls tc-controls";
|
||||
|
||||
.c-conductor__end-fixed {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.c-conductor-input {
|
||||
@ -215,16 +230,39 @@
|
||||
}
|
||||
|
||||
input:invalid {
|
||||
background: rgba($colorFormInvalid, 0.3);
|
||||
background: rgba($colorFormInvalid, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
.is-realtime-mode {
|
||||
button {
|
||||
@include themedButton($colorTimeBg);
|
||||
color: $colorTimeFg;
|
||||
|
||||
&:hover {
|
||||
background: $colorTimeHov !important;
|
||||
color: $colorTimeFg !important;
|
||||
}
|
||||
}
|
||||
|
||||
.c-conductor-input {
|
||||
&:before {
|
||||
color: $colorTime;
|
||||
}
|
||||
}
|
||||
|
||||
.c-conductor__end-fixed {
|
||||
// Displays last RT udpate
|
||||
color: $colorTime;
|
||||
|
||||
input {
|
||||
// Remove input look
|
||||
background: none;
|
||||
box-shadow: none;
|
||||
color: $colorTime;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -396,5 +434,3 @@ export default {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
|
@ -37,6 +37,7 @@
|
||||
@include bgTicks($c: rgba($colorBodyFg, 0.4));
|
||||
background-position: 0 50%;
|
||||
background-size: 5px 2px;
|
||||
border-radius: $controlCr;
|
||||
height: $h;
|
||||
|
||||
svg {
|
||||
@ -76,34 +77,37 @@
|
||||
body.desktop .is-fixed-mode & {
|
||||
@include cursorGrab();
|
||||
background-size: 3px 30%;
|
||||
border-radius: $controlCr;
|
||||
background-color: $colorBodyBgSubtle;
|
||||
box-shadow: inset rgba(black, 0.2) 0 1px 1px;
|
||||
box-shadow: inset rgba(black, 0.4) 0 1px 1px;
|
||||
transition: $transOut;
|
||||
|
||||
svg text {
|
||||
fill: $colorBodyFg;
|
||||
stroke: $colorBodyBgSubtle;
|
||||
transition: $transOut;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:active {
|
||||
$c: $colorKeySubtle;
|
||||
background-color: $c;
|
||||
transition: $transIn;
|
||||
svg text {
|
||||
stroke: $c;
|
||||
transition: $transIn;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.is-realtime-mode & {
|
||||
$c: 1px solid rgba($colorTime, 0.7);
|
||||
border-left: $c;
|
||||
border-right: $c;
|
||||
svg text {
|
||||
fill: $colorTime;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
|
||||
<script>
|
||||
|
@ -21,10 +21,10 @@
|
||||
*****************************************************************************/
|
||||
<template>
|
||||
<div class="c-ctrl-wrapper c-ctrl-wrapper--menus-up">
|
||||
<div class="c-button--menu c-mode-button"
|
||||
<button class="c-button--menu c-mode-button"
|
||||
@click="toggleMenu($event)">
|
||||
<span class="c-button__label">{{selectedMode.name}}</span>
|
||||
</div>
|
||||
</button>
|
||||
<div class="c-menu c-super-menu c-conductor__mode-menu"
|
||||
v-if="showMenu">
|
||||
<div class="c-super-menu__menu">
|
||||
@ -66,16 +66,6 @@
|
||||
min-width: 200px;
|
||||
}
|
||||
}
|
||||
|
||||
.is-realtime-mode {
|
||||
.c-mode-button {
|
||||
background: $colorTimeBg;
|
||||
|
||||
&:hover {
|
||||
background: $colorTimeHov;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
|
@ -22,11 +22,11 @@
|
||||
<template>
|
||||
<div class="c-ctrl-wrapper c-ctrl-wrapper--menus-up"
|
||||
v-if="selectedTimeSystem.name">
|
||||
<div class="c-button--menu c-time-system-button"
|
||||
<button class="c-button--menu c-time-system-button"
|
||||
:class="selectedTimeSystem.cssClass"
|
||||
@click="toggleMenu($event)">
|
||||
<span class="c-button__label">{{selectedTimeSystem.name}}</span>
|
||||
</div>
|
||||
</button>
|
||||
<div class="c-menu" v-if="showMenu">
|
||||
<ul>
|
||||
<li @click="setTimeSystemFromView(timeSystem)"
|
||||
@ -40,20 +40,6 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@import "~styles/sass-base";
|
||||
|
||||
.is-realtime-mode {
|
||||
.c-time-system-button {
|
||||
background: $colorTimeBg;
|
||||
|
||||
&:hover {
|
||||
background: $colorTimeHov;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
inject: ['openmct', 'configuration'],
|
||||
|
@ -108,7 +108,7 @@
|
||||
grid-gap: 1px;
|
||||
height: 100%;
|
||||
|
||||
$mutedOpacity: 0.7;
|
||||
$mutedOpacity: 0.5;
|
||||
|
||||
ul {
|
||||
display: contents;
|
||||
@ -127,7 +127,7 @@
|
||||
padding: $interiorMargin;
|
||||
|
||||
&.is-in-month {
|
||||
background: rgba($colorBodyFg, 0.1);
|
||||
background: $colorMenuElementHilite;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -58,7 +58,7 @@ define(['EventEmitter'], function (EventEmitter) {
|
||||
this.selected[0].element.classList.remove('s-selected');
|
||||
}
|
||||
|
||||
if (this.selected[1]) {
|
||||
if (this.selected[1] && this.selected[1].element) {
|
||||
this.selected[1].element.classList.remove('s-selected-parent');
|
||||
}
|
||||
|
||||
@ -66,7 +66,7 @@ define(['EventEmitter'], function (EventEmitter) {
|
||||
selectable[0].element.classList.add('s-selected');
|
||||
}
|
||||
|
||||
if (selectable[1]) {
|
||||
if (selectable[1] && selectable[1].element) {
|
||||
selectable[1].element.classList.add('s-selected-parent');
|
||||
}
|
||||
|
||||
@ -132,7 +132,7 @@ define(['EventEmitter'], function (EventEmitter) {
|
||||
}
|
||||
|
||||
return function () {
|
||||
element.removeEventListener('click', capture);
|
||||
element.removeEventListener('click', capture, true);
|
||||
element.removeEventListener('click', selectCapture);
|
||||
|
||||
if (unlisten) {
|
||||
|
330
src/styles-new/_constants-espresso.scss
Normal file
330
src/styles-new/_constants-espresso.scss
Normal file
@ -0,0 +1,330 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
/************************************************** ESPRESSO THEME CONSTANTS */
|
||||
|
||||
@import "constants";
|
||||
|
||||
// Functions
|
||||
@function buttonBg($c: $colorBtnBg) {
|
||||
@return linear-gradient(lighten($c, 5%), $c);
|
||||
}
|
||||
|
||||
// Constants
|
||||
$fontBaseSize: 12px;
|
||||
$smallCr: 2px;
|
||||
$controlCr: 3px;
|
||||
$basicCr: 4px;
|
||||
|
||||
// Base colors
|
||||
$colorBodyBg: #393939;
|
||||
$colorBodyFg: #aaa;
|
||||
$colorGenBg: #222;
|
||||
$colorHeadBg: #262626;
|
||||
$colorHeadFg: $colorBodyFg;
|
||||
$colorStatusBarBg: $colorHeadBg;
|
||||
$colorStatusBarFg: $colorBodyFg;
|
||||
$colorStatusBarFgHov: #aaa;
|
||||
$colorKey: #0099cc;
|
||||
$colorKeyFg: #fff;
|
||||
$colorKeyHov: #00c0f6;
|
||||
$colorKeyFilter: brightness(1) sepia(1) hue-rotate(145deg) saturate(6);
|
||||
$colorKeyFilterHov: brightness(1) sepia(1) hue-rotate(145deg) saturate(7);
|
||||
$colorInteriorBorder: rgba($colorBodyFg, 0.2);
|
||||
$colorA: #ccc;
|
||||
$colorAHov: #fff;
|
||||
|
||||
// Layout
|
||||
$shellMainPad: 4px 0;
|
||||
$shellPanePad: $interiorMargin, 7px; // TODO: Sync this with Snow theme
|
||||
|
||||
// Status colors, mainly used for messaging and item ancillary symbols
|
||||
$colorStatusFg: #999;
|
||||
$colorStatusDefault: #ccc;
|
||||
$colorStatusInfo: #60ba7b;
|
||||
$colorStatusAlert: #ffb66c;
|
||||
$colorStatusError: #da0004;
|
||||
$colorStatusBtnBg: #666;
|
||||
|
||||
// States
|
||||
$colorPausedBg: #ff9900;
|
||||
$colorPausedFg: #fff;
|
||||
$colorOk: #33cc33;
|
||||
|
||||
// Base variations
|
||||
$colorBodyBgSubtle: lighten($colorBodyBg, 5%);
|
||||
$colorBodyBgSubtleHov: darken($colorKey, 50%);
|
||||
$colorKeySubtle: darken($colorKey, 10%);
|
||||
|
||||
// Time Colors
|
||||
$colorTime: #618cff;
|
||||
$colorTimeBg: $colorTime;
|
||||
$colorTimeFg: lighten($colorTimeBg, 30%);
|
||||
$colorTimeHov: lighten($colorTime, 10%);
|
||||
$colorTimeSubtle: darken($colorTime, 20%);
|
||||
$colorTOI: $colorBodyFg; // was $timeControllerToiLineColor
|
||||
$colorTOIHov: $colorTime; // was $timeControllerToiLineColorHov
|
||||
|
||||
// Edit Colors
|
||||
$editColor: #00c7c3;
|
||||
$editColorFg: $colorBodyFg;
|
||||
$browseBorderSelectableHov: 1px dotted rgba($colorBodyFg, 0.2);
|
||||
$browseShdwSelectableHov: rgba($colorBodyFg, 0.2) 0 0 3px;
|
||||
$browseBorderSelected: 1px solid rgba($colorBodyFg, 0.6);
|
||||
$editBorderSelectable: 1px dotted rgba($editColor, 1);
|
||||
$editBorderSelectableHov: 1px dashed rgba($editColor, 1);
|
||||
$editBorderSelected: 1px solid $editColor;
|
||||
$editBorderDrilledIn: 1px dashed #ff4d9a;
|
||||
$colorGridLines: rgba($editColor, 0.2);
|
||||
|
||||
// UI elements
|
||||
$colorIconAlias: #4af6f3;
|
||||
$colorIconAliasForKeyFilter: #aaa;
|
||||
$colorProgressBarOuter: rgba(#000, 0.1);
|
||||
$colorProgressBarAmt: #0a0;
|
||||
|
||||
// Buttons and Controls
|
||||
$colorBtnBg: lighten($colorBodyBg, 10%); // !
|
||||
$colorBtnBgHov: lighten($colorBtnBg, 10%); // !
|
||||
$colorBtnFg: lighten($colorBodyFg, 10%); // !
|
||||
$colorBtnFgHov: $colorBtnFg;
|
||||
$colorBtnMajorBg: $colorKey;
|
||||
$colorBtnMajorBgHov: $colorKeyHov;
|
||||
$colorBtnMajorFg: $colorKeyFg;
|
||||
$colorBtnMajorFgHov: darken($colorBtnMajorFg, 10%);
|
||||
$colorBtnCautionBg: #f16f6f;
|
||||
$colorBtnCautionBgHov: #f1504e;
|
||||
$colorBtnCautionFg: $colorBtnFg;
|
||||
$colorClickIcon: $colorKey;
|
||||
$colorClickIconBgHov: rgba($colorKey, 0.6);
|
||||
$colorClickIconFgHov: $colorKeyHov;
|
||||
|
||||
// Menus
|
||||
$colorMenuBg: lighten($colorBodyBg, 20%);
|
||||
$colorMenuFg: lighten($colorBodyFg, 30%);
|
||||
$colorMenuIc: lighten($colorKey, 15%);
|
||||
$colorMenuHovBg: $colorMenuIc;
|
||||
$colorMenuHovFg: lighten($colorMenuFg, 10%);
|
||||
$colorMenuHovIc: $colorMenuHovFg;
|
||||
$colorMenuElementHilite: lighten($colorMenuBg, 10%);
|
||||
$shdwMenu: rgba(black, 0.5) 0 1px 5px;
|
||||
$shdwMenuText: none;
|
||||
$menuItemPad: $interiorMargin, floor($interiorMargin * 1.25);
|
||||
|
||||
// Palettes and Swatches
|
||||
$paletteItemBorderOuterColorSelected: black;
|
||||
$paletteItemBorderInnerColorSelected: white;
|
||||
$paletteItemBorderInnerColor: rgba($paletteItemBorderOuterColorSelected, 0.3);
|
||||
|
||||
// Form colors
|
||||
$colorCheck: $colorKey;
|
||||
$colorFormRequired: $colorKey;
|
||||
$colorFormValid: $colorOk;
|
||||
$colorFormError: #990000;
|
||||
$colorFormInvalid: #ff2200;
|
||||
$colorFormFieldErrorBg: $colorFormError;
|
||||
$colorFormFieldErrorFg: rgba(#fff, 0.6);
|
||||
$colorFormLines: rgba(#000, 0.1);
|
||||
$colorFormSectionHeader: rgba(#000, 0.05);
|
||||
$colorInputBg: rgba(black, 0.2);
|
||||
$colorInputFg: $colorBodyFg;
|
||||
$colorInputPlaceholder: darken($colorBodyFg, 20%);
|
||||
$colorFormText: darken($colorBodyFg, 10%);
|
||||
$colorInputIcon: darken($colorBodyFg, 25%);
|
||||
$colorFieldHint: lighten($colorBodyFg, 40%);
|
||||
$shdwInput: inset rgba(black, 0.4) 0 0 1px;
|
||||
$shdwInputHov: inset rgba(black, 0.7) 0 0 2px;
|
||||
$shdwInputFoc: inset rgba(black, 0.8) 0 0.25px 3px;
|
||||
|
||||
// Inspector
|
||||
$colorInspectorBg: lighten($colorBodyBg, 5%);
|
||||
$colorInspectorFg: $colorBodyFg;
|
||||
$colorInspectorPropName: darken($colorBodyFg, 20%);
|
||||
$colorInspectorPropVal: lighten($colorInspectorFg, 15%);
|
||||
$colorInspectorSectionHeaderBg: lighten($colorInspectorBg, 5%);
|
||||
$colorInspectorSectionHeaderFg: lighten($colorInspectorBg, 40%);
|
||||
|
||||
// Overlay
|
||||
$overlayColorBg: $colorMenuBg;
|
||||
$overlayColorFg: $colorMenuFg;
|
||||
$overlayButtonColorBg: lighten($overlayColorBg, 20%);
|
||||
$overlayButtonColorFg: #fff;
|
||||
$overlayCr: $interiorMarginLg;
|
||||
|
||||
// Indicator colors
|
||||
$colorIndicatorAvailable: $colorKey;
|
||||
$colorIndicatorDisabled: #444;
|
||||
$colorIndicatorOn: $colorOk;
|
||||
$colorIndicatorOff: #666;
|
||||
|
||||
// Limits and staleness colors//
|
||||
$colorTelemFresh: lighten($colorBodyFg, 20%);
|
||||
$colorTelemStale: darken($colorBodyFg, 20%);
|
||||
$styleTelemStale: italic;
|
||||
$colorLimitYellowBg: rgba(#ffaa00, 0.3);
|
||||
$colorLimitYellowIc: #ffaa00;
|
||||
$colorLimitRedBg: rgba(red, 0.3);
|
||||
$colorLimitRedIc: red;
|
||||
|
||||
// Bubble colors
|
||||
$colorInfoBubbleBg: $colorMenuBg;
|
||||
$colorInfoBubbleFg: #666;
|
||||
|
||||
// Items
|
||||
$colorItemBg: buttonBg($colorBtnBg);
|
||||
$colorItemBgHov: buttonBg(lighten($colorBtnBg, 5%));
|
||||
$colorListItemBg: transparent;
|
||||
$colorListItemBgHov: rgba($colorKey, 0.1);
|
||||
$colorItemFg: $colorBtnFg;
|
||||
$colorItemFgDetails: darken($colorItemFg, 20%);
|
||||
$shdwItemText: none;
|
||||
|
||||
// Tabular
|
||||
$colorTabBorder: lighten($colorBodyBg, 10%);
|
||||
$colorTabBodyBg: $colorBodyBg;
|
||||
$colorTabBodyFg: lighten($colorBodyFg, 20%);
|
||||
$colorTabHeaderBg: lighten($colorBodyBg, 10%);
|
||||
$colorTabHeaderFg: lighten($colorBodyFg, 20%);
|
||||
$colorTabHeaderBorder: $colorBodyBg;
|
||||
|
||||
// Plot
|
||||
$colorPlotBg: rgba(black, 0.05);
|
||||
$colorPlotFg: $colorBodyFg;
|
||||
$colorPlotHash: black;
|
||||
$opacityPlotHash: 0.2;
|
||||
$stylePlotHash: dashed;
|
||||
$colorPlotAreaBorder: $colorInteriorBorder;
|
||||
$colorPlotLabelFg: darken($colorPlotFg, 20%);
|
||||
$legendCollapsedNameMaxW: 50%;
|
||||
$legendHoverValueBg: rgba($colorBodyFg, 0.2);
|
||||
|
||||
// Tree
|
||||
$colorTreeBg: transparent;
|
||||
$colorItemTreeHoverBg: lighten($colorBodyBg, 10%);
|
||||
$colorItemTreeHoverFg: lighten($colorBodyFg, 10%);
|
||||
$colorItemTreeIcon: $colorKey; // Used
|
||||
$colorItemTreeIconHover: $colorItemTreeIcon; // Used
|
||||
$colorItemTreeFg: $colorBodyFg;
|
||||
$colorItemTreeSelectedBg: darken($colorKey, 15%);
|
||||
$colorItemTreeSelectedFg: $colorBodyBg;
|
||||
$colorItemTreeEditingBg: $editColor;
|
||||
$colorItemTreeEditingFg: $editColorFg;
|
||||
$colorItemTreeVC: $colorBodyFg;
|
||||
$colorItemTreeVCHover: $colorKey;
|
||||
$shdwItemTreeIcon: none;
|
||||
|
||||
// Images
|
||||
$colorThumbHoverBg: $colorItemTreeHoverBg;
|
||||
|
||||
// Scrollbar
|
||||
$scrollbarTrackSize: 7px;
|
||||
$scrollbarTrackShdw: rgba(#000, 0.2) 0 1px 2px;
|
||||
$scrollbarTrackColorBg: rgba(#000, 0.2);
|
||||
$scrollbarThumbColor: darken($colorBodyBg, 50%);
|
||||
$scrollbarThumbColorHov: $colorKey;
|
||||
$scrollbarThumbColorMenu: lighten($colorMenuBg, 10%);
|
||||
$scrollbarThumbColorMenuHov: lighten($scrollbarThumbColorMenu, 2%);
|
||||
|
||||
// Splitter
|
||||
$splitterHandleD: 2px;
|
||||
$splitterHandleHitMargin: 4px;
|
||||
$colorSplitterBaseBg: $colorBodyBg;
|
||||
$colorSplitterBg: lighten($colorSplitterBaseBg, 10%);
|
||||
$colorSplitterFg: $colorBodyBg;
|
||||
$colorSplitterHover: $colorKey;
|
||||
$colorSplitterActive: $colorKey;
|
||||
$splitterBtnD: (16px, 35px); // height, width
|
||||
$splitterBtnColorBg: $colorBtnBg;
|
||||
$splitterBtnColorFg: #999;
|
||||
$splitterBtnLabelColorFg: #666;
|
||||
$splitterCollapsedBtnColorBg: #222;
|
||||
$splitterCollapsedBtnColorFg: #666;
|
||||
$splitterCollapsedBtnColorBgHov: $colorKey;
|
||||
$splitterCollapsedBtnColorFgHov: $colorKeyFg;
|
||||
|
||||
// Mobile
|
||||
$colorMobilePaneLeft: darken($colorBodyBg, 2%);
|
||||
$colorMobilePaneLeftTreeItemBg: rgba($colorBodyFg, 0.1);
|
||||
$colorMobilePaneLeftTreeItemFg: $colorItemTreeFg;
|
||||
$colorMobileSelectListTreeItemBg: rgba(#000, 0.05);
|
||||
|
||||
// About Screen
|
||||
$colorAboutLink: $colorKeySubtle;
|
||||
|
||||
// Loading
|
||||
$colorLoadingFg: #776ba2;
|
||||
$colorLoadingBg: rgba($colorLoadingFg, 0.1);
|
||||
|
||||
// Transitions
|
||||
$transIn: all 50ms ease-in-out;
|
||||
$transOut: all 250ms ease-in-out;
|
||||
$transInBounce: all 200ms cubic-bezier(.47,.01,.25,1.5);
|
||||
$transInBounceBig: all 300ms cubic-bezier(.2,1.6,.6,3);
|
||||
|
||||
// Discrete items, like Notebook entries, Widget rules
|
||||
$createBtnTextTransform: uppercase;
|
||||
|
||||
@mixin discreteItem() {
|
||||
background: rgba($colorBodyFg,0.1);
|
||||
border: none;
|
||||
border-radius: $controlCr;
|
||||
|
||||
.c-input-inline:hover {
|
||||
background: $colorBodyBg;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin discreteItemInnerElem() {
|
||||
border: 1px solid rgba(#fff, 0.1);
|
||||
border-radius: $controlCr;
|
||||
}
|
||||
|
||||
@mixin themedButton($c: $colorBtnBg) {
|
||||
background: linear-gradient(lighten($c, 5%), $c);
|
||||
box-shadow: rgba(black, 0.5) 0 0 2px;
|
||||
}
|
||||
|
||||
/**************************************************** NOT USED, LEAVE FOR NOW */
|
||||
// Slider controls, not in use
|
||||
/*
|
||||
$sliderColorBase: $colorKey;
|
||||
$sliderColorRangeHolder: rgba(black, 0.07);
|
||||
$sliderColorRange: rgba($sliderColorBase, 0.2);
|
||||
$sliderColorRangeHov: rgba($sliderColorBase, 0.4);
|
||||
$sliderColorKnob: darken($sliderColorBase, 20%);
|
||||
$sliderColorKnobHov: rgba($sliderColorBase, 0.7);
|
||||
$sliderColorRangeValHovBg: $sliderColorRange;
|
||||
$sliderColorRangeValHovFg: $colorBodyFg;
|
||||
$sliderKnobW: 15px;
|
||||
$sliderKnobR: 2px;
|
||||
*/
|
||||
|
||||
// Content status
|
||||
/*
|
||||
$colorAlert: #ff3c00;
|
||||
$colorWarningHi: #990000;
|
||||
$colorWarningLo: #ff9900;
|
||||
$colorDiagnostic: #a4b442;
|
||||
$colorCommand: #3693bd;
|
||||
$colorInfo: #2294a2;
|
||||
$colorOk: #33cc33;
|
||||
*/
|
@ -20,115 +20,73 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/****************************************************** SNOW THEME CONSTANTS */
|
||||
|
||||
@import "constants";
|
||||
|
||||
// Functions
|
||||
@function pullForward($c: $colorBodyBg, $p: 20%) {
|
||||
// For dark interfaces, lighter things come forward - opposite for light interfaces
|
||||
@return darken($c, $p);
|
||||
@function buttonBg($c: $colorBtnBg) {
|
||||
@return $c;
|
||||
}
|
||||
|
||||
@function pushBack($c: $colorBodyBg, $p: 20%) {
|
||||
// For dark interfaces, darker things move back - opposite for light interfaces
|
||||
@return lighten($c, $p);
|
||||
}
|
||||
|
||||
// Global
|
||||
// Constants
|
||||
$fontBaseSize: 12px;
|
||||
$smallCr: 2px;
|
||||
$controlCr: 3px;
|
||||
$basicCr: 4px;
|
||||
|
||||
// Base colors
|
||||
$colorBodyBg: #fcfcfc;
|
||||
$colorBodyFg: #666;
|
||||
$colorGenBg: #fff;
|
||||
$colorHeadBg: #eee;
|
||||
$colorHeadFg: $colorBodyFg;
|
||||
$colorStatusBarBg: #000;
|
||||
$colorStatusBarFg: #999;
|
||||
$colorStatusBarFgHov: #aaa;
|
||||
$colorKey: #0099cc;
|
||||
$colorKeyFilter: brightness(0.9) sepia(1) hue-rotate(145deg) saturate(6);
|
||||
$colorKeyFilterHov: brightness(1) sepia(1) hue-rotate(145deg) saturate(7);
|
||||
$colorKeySelectedBg: $colorKey;
|
||||
$colorKeyFg: #fff;
|
||||
$colorKeyHov: #00c0f6;
|
||||
$colorEditAreaBg: #eafaff; // Deprecated, use $editColor instead
|
||||
$colorEditAreaFg: #4bb1c7; // Deprecated, use $editColor instead
|
||||
$colorKeyFilter: brightness(0.9) sepia(1) hue-rotate(145deg) saturate(6);
|
||||
$colorKeyFilterHov: brightness(1) sepia(1) hue-rotate(145deg) saturate(7);
|
||||
$colorInteriorBorder: rgba($colorBodyFg, 0.2);
|
||||
$colorA: #999;
|
||||
$colorAHov: $colorKey;
|
||||
$contrastRatioPercent: 40%;
|
||||
$hoverRatioPercent: 10%;
|
||||
$basicCr: 4px;
|
||||
$controlCr: 3px;
|
||||
$smallCr: 2px;
|
||||
$overlayCr: 11px;
|
||||
$shdwTextSubtle: rgba(black, 0.2) 0 1px 2px;
|
||||
|
||||
// Variations
|
||||
$colorBodyBgSubtle: pullForward($colorBodyBg, 5%);
|
||||
$colorBodyBgSubtleHov: pushBack($colorKey, 50%);
|
||||
$colorKeySubtle: pushBack($colorKey, 50%);
|
||||
// Layout
|
||||
$shellMainPad: 4px 0;
|
||||
$shellPanePad: 0 7px;
|
||||
|
||||
// Status colors, mainly used for messaging and item ancillary symbols
|
||||
$colorStatusFg: #999;
|
||||
$colorStatusDefault: #ccc;
|
||||
$colorStatusInfo: #60ba7b;
|
||||
$colorStatusAlert: #ffb66c;
|
||||
$colorStatusError: #da0004;
|
||||
$colorStatusBtnBg: #666;
|
||||
|
||||
// States
|
||||
$colorPausedBg: #ff9900;
|
||||
$colorPausedFg: #fff;
|
||||
$colorOk: #33cc33;
|
||||
|
||||
// Base variations
|
||||
$colorBodyBgSubtle: darken($colorBodyBg, 5%);
|
||||
$colorBodyBgSubtleHov: lighten($colorKey, 50%);
|
||||
$colorKeySubtle: lighten($colorKey, 50%);
|
||||
|
||||
// Time Colors
|
||||
$colorTime: #618cff;
|
||||
$colorTimeBg: $colorTime;
|
||||
$colorTimeFg: $colorBodyBg;
|
||||
$colorTimeHov: pushBack($colorTime, 5%);
|
||||
$colorTimeSubtle: pushBack($colorTime, 20%);
|
||||
|
||||
// Buttons and Controls
|
||||
$btnPad: $interiorMargin, $interiorMargin * 1.25;
|
||||
$colorBtnBg: #aaaaaa;
|
||||
$colorBtnBgHov: pullForward($colorBtnBg, $hoverRatioPercent);
|
||||
$colorBtnFg: #fff;
|
||||
$colorBtnFgHov: $colorBtnFg;
|
||||
$colorBtnIcon: #eee;
|
||||
$colorBtnIconHov: $colorBtnFgHov;
|
||||
$colorBtnMajorBg: $colorKey;
|
||||
$colorBtnMajorBgHov: $colorKeyHov;
|
||||
$colorBtnMajorFg: $colorKeyFg;
|
||||
$colorBtnMajorFgHov: pushBack($colorBtnMajorFg, $hoverRatioPercent);
|
||||
$colorBtnCautionBg: #f16f6f;
|
||||
$colorBtnCautionBgHov: #f1504e;
|
||||
$colorBtnCautionFg: $colorBtnFg;
|
||||
$colorClickIcon: $colorKey;
|
||||
$colorClickIconHov: $colorKeyHov;
|
||||
$colorToggleIcon: rgba($colorClickIcon, 0.5);
|
||||
$colorToggleIconActive: $colorKey;
|
||||
$colorToggleIconHov: rgba($colorToggleIconActive, 0.5);
|
||||
$colorInvokeMenu: #000;
|
||||
$contrastInvokeMenuPercent: 40%;
|
||||
$shdwBtns: none;
|
||||
$shdwBtnsOverlay: none;
|
||||
$sliderColorBase: $colorKey;
|
||||
$sliderColorRangeHolder: rgba(black, 0.07);
|
||||
$sliderColorRange: rgba($sliderColorBase, 0.2);
|
||||
$sliderColorRangeHov: rgba($sliderColorBase, 0.4);
|
||||
$sliderColorKnob: pushBack($sliderColorBase, 20%);
|
||||
$sliderColorKnobHov: rgba($sliderColorBase, 0.7);
|
||||
$sliderColorRangeValHovBg: $sliderColorRange;
|
||||
$sliderColorRangeValHovFg: $colorBodyFg;
|
||||
$sliderKnobW: 15px;
|
||||
$sliderKnobR: 2px;
|
||||
$timeControllerToiLineColor: $colorBodyFg;
|
||||
$timeControllerToiLineColorHov: $colorTime;
|
||||
$colorTransLucBg: #666; // Used as a visual blocking element over variable backgrounds, like imagery
|
||||
$createBtnTextTransform: uppercase;
|
||||
|
||||
// Foundation Colors
|
||||
$colorAlt1: #776ba2;
|
||||
$colorAlert: #ff3c00;
|
||||
$colorWarningHi: #990000;
|
||||
$colorWarningLo: #ff9900;
|
||||
$colorDiagnostic: #a4b442;
|
||||
$colorCommand: #3693bd;
|
||||
$colorInfo: #2294a2;
|
||||
$colorOk: #33cc33;
|
||||
$colorIconAlias: #4af6f3;
|
||||
$colorIconAliasForKeyFilter: #aaa;
|
||||
$colorPausedBg: #ff9900;
|
||||
$colorPausedFg: #fff;
|
||||
$colorCreateBtn: $colorKey;
|
||||
$colorInvokeMenu: #fff;
|
||||
$colorObjHdrTxt: $colorBodyFg;
|
||||
$colorObjHdrIc: lighten($colorObjHdrTxt, 30%);
|
||||
$colorTick: rgba(black, 0.2);
|
||||
$colorTimeHov: lighten($colorTime, 5%);
|
||||
$colorTimeSubtle: lighten($colorTime, 20%);
|
||||
$colorTOI: $colorBodyFg; // was $timeControllerToiLineColor
|
||||
$colorTOIHov: $colorTime; // was $timeControllerToiLineColorHov
|
||||
|
||||
// Edit Colors
|
||||
$editColor: #00c7c3;
|
||||
$editColorFg: $colorBodyFg;
|
||||
$browseBorderSelectableHov: 1px dotted rgba($colorBodyFg, 0.2);
|
||||
$browseShdwSelectableHov: rgba($colorBodyFg, 0.2) 0 0 3px;
|
||||
$browseBorderSelected: 1px solid rgba($colorBodyFg, 0.6);
|
||||
@ -138,18 +96,39 @@ $editBorderSelected: 1px solid $editColor;
|
||||
$editBorderDrilledIn: 1px dashed #ff4d9a;
|
||||
$colorGridLines: rgba($editColor, 0.2);
|
||||
|
||||
// UI elements
|
||||
$colorIconAlias: #4af6f3;
|
||||
$colorIconAliasForKeyFilter: #aaa;
|
||||
$colorProgressBarOuter: rgba(#000, 0.1);
|
||||
$colorProgressBarAmt: #0a0;
|
||||
|
||||
// Buttons and Controls
|
||||
$colorBtnBg: #aaaaaa;
|
||||
$colorBtnBgHov: darken($colorBtnBg, 10%);
|
||||
$colorBtnFg: #fff;
|
||||
$colorBtnFgHov: $colorBtnFg;
|
||||
$colorBtnMajorBg: $colorKey;
|
||||
$colorBtnMajorBgHov: $colorKeyHov;
|
||||
$colorBtnMajorFg: $colorKeyFg;
|
||||
$colorBtnMajorFgHov: lighten($colorBtnMajorFg, 10%);
|
||||
$colorBtnCautionBg: #f16f6f;
|
||||
$colorBtnCautionBgHov: #f1504e;
|
||||
$colorBtnCautionFg: $colorBtnFg;
|
||||
$colorClickIcon: $colorKey;
|
||||
$colorClickIconBgHov: rgba($colorKey, 0.2);
|
||||
$colorClickIconFgHov: $colorKeyHov;
|
||||
|
||||
// Menus
|
||||
$colorMenuBg: pushBack($colorBodyBg, 10%);
|
||||
$colorMenuFg: pullForward($colorMenuBg, 70%);
|
||||
$colorMenuBg: lighten($colorBodyBg, 10%);
|
||||
$colorMenuFg: darken($colorMenuBg, 70%);
|
||||
$colorMenuIc: $colorKey;
|
||||
$colorMenuHovBg: $colorMenuIc; //pullForward($colorMenuBg, $hoverRatioPercent);
|
||||
$colorMenuHovBg: $colorMenuIc;
|
||||
$colorMenuHovFg: $colorMenuBg;
|
||||
$colorMenuHovIc: $colorMenuBg;
|
||||
$colorMenuElementHilite: darken($colorMenuBg, 10%);
|
||||
$shdwMenu: rgba(black, 0.5) 0 1px 5px;
|
||||
$shdwMenuText: none;
|
||||
$colorCreateMenuLgIcon: $colorKey;
|
||||
$colorCreateMenuText: $colorBodyFg;
|
||||
$menuItemPad: ($interiorMargin, nth($btnPad, 2));
|
||||
$menuItemPad: $interiorMargin, floor($interiorMargin * 1.25);
|
||||
|
||||
// Palettes and Swatches
|
||||
$paletteItemBorderOuterColorSelected: black;
|
||||
@ -160,7 +139,7 @@ $paletteItemBorderInnerColor: rgba($paletteItemBorderOuterColorSelected, 0.3);
|
||||
$colorCheck: $colorKey;
|
||||
$colorFormRequired: $colorKey;
|
||||
$colorFormValid: $colorOk;
|
||||
$colorFormError: $colorWarningHi;
|
||||
$colorFormError: #990000;
|
||||
$colorFormInvalid: #ff2200;
|
||||
$colorFormFieldErrorBg: $colorFormError;
|
||||
$colorFormFieldErrorFg: rgba(#fff, 0.6);
|
||||
@ -168,35 +147,28 @@ $colorFormLines: rgba(#000, 0.1);
|
||||
$colorFormSectionHeader: rgba(#000, 0.05);
|
||||
$colorInputBg: $colorGenBg;
|
||||
$colorInputFg: $colorBodyFg;
|
||||
$colorInputPlaceholder: pushBack($colorBodyFg, 20%);
|
||||
$colorFormText: pushBack($colorBodyFg, 10%);
|
||||
$colorInputIcon: pushBack($colorBodyFg, 25%);
|
||||
$colorFieldHint: pullForward($colorBodyFg, 40%);
|
||||
$shdwInput: inset rgba(black, 0.4) 0 0 1px;
|
||||
$shdwInputHov: inset rgba(black, 0.7) 0 0 1px;
|
||||
$shdwInputFoc: inset rgba(black, 0.7) 0 0 3px;
|
||||
$colorInputPlaceholder: lighten($colorBodyFg, 20%);
|
||||
$colorFormText: lighten($colorBodyFg, 10%);
|
||||
$colorInputIcon: lighten($colorBodyFg, 25%);
|
||||
$colorFieldHint: darken($colorBodyFg, 40%);
|
||||
$shdwInput: inset rgba(black, 0.7) 0 0 1px;
|
||||
$shdwInputHov: inset rgba(black, 0.7) 0 0 2px;
|
||||
$shdwInputFoc: inset rgba(black, 0.8) 0 0.25px 3px;
|
||||
|
||||
// Inspector
|
||||
$colorInspectorBg: pullForward($colorBodyBg, 5%);
|
||||
$colorInspectorBg: darken($colorBodyBg, 5%);
|
||||
$colorInspectorFg: $colorBodyFg;
|
||||
$colorInspectorPropName: pushBack($colorBodyFg, 20%);
|
||||
$colorInspectorPropVal: pullForward($colorInspectorFg, 15%);
|
||||
$colorInspectorSectionHeaderBg: pullForward($colorInspectorBg, 5%);
|
||||
$colorInspectorSectionHeaderFg: pullForward($colorInspectorBg, 40%);
|
||||
$colorInspectorPropName: lighten($colorBodyFg, 20%);
|
||||
$colorInspectorPropVal: darken($colorInspectorFg, 15%);
|
||||
$colorInspectorSectionHeaderBg: darken($colorInspectorBg, 5%);
|
||||
$colorInspectorSectionHeaderFg: darken($colorInspectorBg, 40%);
|
||||
|
||||
// Status colors, mainly used for messaging and item ancillary symbols
|
||||
$colorStatusFg: #999;
|
||||
$colorStatusDefault: #ccc;
|
||||
$colorStatusInfo: #60ba7b;
|
||||
$colorStatusAlert: #ffb66c;
|
||||
$colorStatusError: #da0004;
|
||||
$colorStatusBtnBg: #666;
|
||||
$colorProgressBarOuter: rgba(#000, 0.1);
|
||||
$colorProgressBarAmt: #0a0;
|
||||
$progressBarHOverlay: 15px;
|
||||
$progressBarStripeW: 20px;
|
||||
$shdwStatusIc: rgba(white, 0.8) 0 0px 5px;
|
||||
$animPausedPulseDur: 1s;
|
||||
// Overlay
|
||||
$overlayColorBg: $colorMenuBg;
|
||||
$overlayColorFg: $colorMenuFg;
|
||||
$overlayButtonColorBg: $colorBtnBg;
|
||||
$overlayButtonColorFg: $colorBtnFg;
|
||||
$overlayCr: $interiorMarginLg;
|
||||
|
||||
// Indicator colors
|
||||
$colorIndicatorAvailable: $colorKey;
|
||||
@ -204,13 +176,9 @@ $colorIndicatorDisabled: #444;
|
||||
$colorIndicatorOn: $colorOk;
|
||||
$colorIndicatorOff: #666;
|
||||
|
||||
// Selects
|
||||
$colorSelectBg: $colorBtnBg;
|
||||
$colorSelectFg: $colorBtnFg;
|
||||
|
||||
// Limits and staleness colors//
|
||||
$colorTelemFresh: pullForward($colorBodyFg, 20%);
|
||||
$colorTelemStale: pushBack($colorBodyFg, 20%);
|
||||
$colorTelemFresh: darken($colorBodyFg, 20%);
|
||||
$colorTelemStale: lighten($colorBodyFg, 20%);
|
||||
$styleTelemStale: italic;
|
||||
$colorLimitYellowBg: rgba(#ffaa00, 0.3);
|
||||
$colorLimitYellowIc: #ffaa00;
|
||||
@ -220,37 +188,22 @@ $colorLimitRedIc: red;
|
||||
// Bubble colors
|
||||
$colorInfoBubbleBg: $colorMenuBg;
|
||||
$colorInfoBubbleFg: #666;
|
||||
$colorThumbsBubbleFg: pullForward($colorBodyFg, 10%);
|
||||
$colorThumbsBubbleBg: pullForward($colorBodyBg, 10%);
|
||||
|
||||
// Overlay
|
||||
$colorOvrBlocker: rgba(black, 0.7);//
|
||||
$colorOvrBg: $colorBodyBg;
|
||||
$colorOvrFg: $colorBodyFg;
|
||||
$colorOvrBtnBg: pullForward($colorOvrBg, 40%);
|
||||
$colorOvrBtnFg: #fff;
|
||||
$colorFieldHintOverlay: pullForward($colorOvrBg, 40%);
|
||||
$durLargeViewExpand: 250ms;
|
||||
|
||||
// Items
|
||||
$colorItemBg: #ddd;
|
||||
$colorItemBgHov: rgba($colorKey, 0.1); //pushBack($colorItemBg, $hoverRatioPercent * 0.4);
|
||||
$colorItemBg: lighten($colorBtnBg, 20%);
|
||||
$colorItemBgHov: lighten($colorItemBg, 5%);
|
||||
$colorListItemBg: transparent;
|
||||
$colorListItemBgHov: rgba($colorKey, 0.1);
|
||||
$colorItemFg: $colorBodyFg;
|
||||
$colorItemFgDetails: pushBack($colorItemFg, 15%);
|
||||
$colorItemIc: $colorKey;
|
||||
$colorItemSubIcons: $colorItemFgDetails;
|
||||
$colorItemOpenIcon: $colorItemFgDetails;
|
||||
$colorItemFgDetails: lighten($colorItemFg, 15%);
|
||||
$shdwItemText: none;
|
||||
$colorItemBgSelected: $colorKey;
|
||||
|
||||
// Tabular
|
||||
$colorTabBorder: pullForward($colorBodyBg, 10%);
|
||||
$colorTabBorder: darken($colorBodyBg, 10%);
|
||||
$colorTabBodyBg: $colorBodyBg;
|
||||
$colorTabBodyFg: pullForward($colorBodyFg, 20%);
|
||||
$colorTabHeaderBg: pullForward($colorBodyBg, 10%);
|
||||
$colorTabHeaderFg: pullForward($colorBodyFg, 20%);
|
||||
$colorTabBodyFg: darken($colorBodyFg, 20%);
|
||||
$colorTabHeaderBg: darken($colorBodyBg, 10%);
|
||||
$colorTabHeaderFg: darken($colorBodyFg, 20%);
|
||||
$colorTabHeaderBorder: $colorBodyBg;
|
||||
|
||||
// Plot
|
||||
@ -260,24 +213,23 @@ $colorPlotHash: black;
|
||||
$opacityPlotHash: 0.2;
|
||||
$stylePlotHash: dashed;
|
||||
$colorPlotAreaBorder: $colorInteriorBorder;
|
||||
$colorPlotLabelFg: pushBack($colorPlotFg, 20%);
|
||||
$colorPlotLabelFg: lighten($colorPlotFg, 20%);
|
||||
$legendCollapsedNameMaxW: 50%;
|
||||
$legendHoverValueBg: rgba($colorBodyFg, 0.2);
|
||||
|
||||
// Tree
|
||||
$colorTreeBg: #f0f0f0; // Used
|
||||
$colorItemTreeHoverBg: pullForward($colorBodyBg, $hoverRatioPercent);
|
||||
$colorItemTreeHoverFg: pullForward($colorBodyFg, $hoverRatioPercent);
|
||||
$colorTreeBg: transparent;
|
||||
$colorItemTreeHoverBg: darken($colorBodyBg, 10%);
|
||||
$colorItemTreeHoverFg: darken($colorBodyFg, 10%);
|
||||
$colorItemTreeIcon: $colorKey; // Used
|
||||
$colorItemTreeIconHover: $colorItemTreeIcon; // Used
|
||||
$colorItemTreeFg: $colorBodyFg;
|
||||
$colorItemTreeSelectedBg: pushBack($colorKey, 15%);
|
||||
$colorItemTreeSelectedBg: lighten($colorKey, 15%);
|
||||
$colorItemTreeSelectedFg: $colorBodyBg;
|
||||
$colorItemTreeEditingBg: #caf1ff;
|
||||
$colorItemTreeEditingFg: $colorEditAreaFg;
|
||||
$colorItemTreeEditingBg: $editColor;
|
||||
$colorItemTreeEditingFg: $editColorFg;
|
||||
$colorItemTreeVC: $colorBodyFg;
|
||||
$colorItemTreeVCHover: $colorKey;
|
||||
$colorItemTreeSelectedVC: $colorBodyBg;
|
||||
$shdwItemTreeIcon: none;
|
||||
|
||||
// Images
|
||||
@ -289,54 +241,37 @@ $scrollbarTrackShdw: rgba(#000, 0.2) 0 1px 2px;
|
||||
$scrollbarTrackColorBg: rgba(#000, 0.2);
|
||||
$scrollbarThumbColor: darken($colorBodyBg, 50%);
|
||||
$scrollbarThumbColorHov: $colorKey;
|
||||
$scrollbarThumbColorOverlay: darken($colorOvrBg, 50%);
|
||||
$scrollbarThumbColorOverlayHov: $scrollbarThumbColorHov;
|
||||
$scrollbarThumbColorMenu: pullForward($colorMenuBg, 10%);
|
||||
$scrollbarThumbColorMenuHov: pullForward($scrollbarThumbColorMenu, 2%);
|
||||
$scrollbarThumbColorMenu: darken($colorMenuBg, 10%);
|
||||
$scrollbarThumbColorMenuHov: darken($scrollbarThumbColorMenu, 2%);
|
||||
|
||||
// Splitter
|
||||
$splitterD: 7px;
|
||||
$splitterHandleD: 2px;
|
||||
$splitterHandleHitMargin: 4px;
|
||||
$splitterGrippyD: ($splitterHandleD - 4, 75px, 50px); // thickness, length, min-length
|
||||
$colorSplitterBaseBg: $colorBodyBg;
|
||||
$colorSplitterBg: pullForward($colorSplitterBaseBg, 20%);
|
||||
$colorSplitterBg: darken($colorSplitterBaseBg, 20%);
|
||||
$colorSplitterFg: $colorBodyBg;
|
||||
$colorSplitterHover: $colorKey; // pullForward($colorSplitterBg, $hoverRatioPercent * 2);
|
||||
$colorSplitterHover: $colorKey;
|
||||
$colorSplitterActive: $colorKey;
|
||||
$splitterBtnD: (16px, 35px); // height, width
|
||||
$splitterBtnColorBg: #eee;
|
||||
$splitterBtnColorFg: #999;
|
||||
$splitterBtnColorHoverBg: rgba($colorKey, 1);
|
||||
$splitterBtnColorHoverFg: $colorBodyBg;
|
||||
$colorSplitterGrippy: pullForward($colorSplitterBaseBg, 30%);
|
||||
$splitterShdw: none;
|
||||
$splitterEndCr: none;
|
||||
|
||||
// Minitabs
|
||||
$colorMiniTabBg: $colorSplitterBg;
|
||||
$colorMiniTabFg: pullForward($colorMiniTabBg, 30%);
|
||||
$colorMiniTabBgHov: $colorSplitterHover;
|
||||
$colorMiniTabFgHov: #fff;
|
||||
$splitterBtnColorBg: $colorBtnBg;
|
||||
$splitterBtnColorFg: #ddd;
|
||||
$splitterBtnLabelColorFg: #999;
|
||||
$splitterCollapsedBtnColorBg: #ccc;
|
||||
$splitterCollapsedBtnColorFg: #666;
|
||||
$splitterCollapsedBtnColorBgHov: $colorKey;
|
||||
$splitterCollapsedBtnColorFgHov: $colorKeyFg;
|
||||
|
||||
// Mobile
|
||||
$colorMobilePaneLeft: darken($colorBodyBg, 2%);
|
||||
$colorMobilePaneLeftTreeItemBg: rgba($colorBodyFg, 0.1); //pullForward($colorMobilePaneLeft, 3%);
|
||||
$colorMobilePaneLeftTreeItemBg: rgba($colorBodyFg, 0.1);
|
||||
$colorMobilePaneLeftTreeItemFg: $colorItemTreeFg;
|
||||
$colorMobileSelectListTreeItemBg: rgba(#000, 0.05);
|
||||
|
||||
// Datetime Picker, Calendar
|
||||
$colorCalCellHovBg: $colorKey;
|
||||
$colorCalCellHovFg: $colorKeyFg;
|
||||
$colorCalCellSelectedBg: $colorItemTreeSelectedBg;
|
||||
$colorCalCellSelectedFg: $colorItemTreeSelectedFg;
|
||||
$colorCalCellInMonthBg: pullForward($colorMenuBg, 5%);
|
||||
|
||||
// About Screen
|
||||
$colorAboutLink: #84b3ff;
|
||||
$colorAboutLink: $colorKeySubtle;
|
||||
|
||||
// Loading
|
||||
$colorLoadingFg: $colorAlt1;
|
||||
$colorLoadingFg: #776ba2;
|
||||
$colorLoadingBg: rgba($colorLoadingFg, 0.1);
|
||||
|
||||
// Transitions
|
||||
@ -346,14 +281,50 @@ $transInBounce: all 200ms cubic-bezier(.47,.01,.25,1.5);
|
||||
$transInBounceBig: all 300ms cubic-bezier(.2,1.6,.6,3);
|
||||
|
||||
// Discrete items, like Notebook entries, Widget rules
|
||||
$createBtnTextTransform: uppercase;
|
||||
|
||||
@mixin discreteItem() {
|
||||
background: rgba($colorBodyFg,0.1);
|
||||
border: 1px solid $colorInteriorBorder;
|
||||
border-radius: $controlCr;
|
||||
|
||||
.c-input-inline:hover {
|
||||
background: $colorBodyBg;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin discreteItemInnerElem() {
|
||||
border: 1px solid $colorBodyBg;
|
||||
border-radius: $controlCr; }
|
||||
border-radius: $controlCr;
|
||||
}
|
||||
|
||||
@mixin themedButton($c: $colorBtnBg) {
|
||||
background: $c;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************** NOT USED, LEAVE FOR NOW */
|
||||
// Slider controls, not in use
|
||||
/*
|
||||
$sliderColorBase: $colorKey;
|
||||
$sliderColorRangeHolder: rgba(black, 0.07);
|
||||
$sliderColorRange: rgba($sliderColorBase, 0.2);
|
||||
$sliderColorRangeHov: rgba($sliderColorBase, 0.4);
|
||||
$sliderColorKnob: lighten($sliderColorBase, 20%);
|
||||
$sliderColorKnobHov: rgba($sliderColorBase, 0.7);
|
||||
$sliderColorRangeValHovBg: $sliderColorRange;
|
||||
$sliderColorRangeValHovFg: $colorBodyFg;
|
||||
$sliderKnobW: 15px;
|
||||
$sliderKnobR: 2px;
|
||||
*/
|
||||
|
||||
// Content status
|
||||
/*
|
||||
$colorAlert: #ff3c00;
|
||||
$colorWarningHi: #990000;
|
||||
$colorWarningLo: #ff9900;
|
||||
$colorDiagnostic: #a4b442;
|
||||
$colorCommand: #3693bd;
|
||||
$colorInfo: #2294a2;
|
||||
$colorOk: #33cc33;
|
||||
*/
|
||||
|
@ -40,17 +40,20 @@ $inputTextP: $inputTextPTopBtm $inputTextPLeftRight;
|
||||
$menuLineH: 1.5rem;
|
||||
$treeItemIndent: 16px;
|
||||
$treeTypeIconW: 18px;
|
||||
$overlayOuterMargin: 5%;
|
||||
$overlayInnerMargin: 25px;
|
||||
|
||||
/*************** Items */
|
||||
$itemPadLR: 5px;
|
||||
$ueBrowseGridItemLg: 200px;
|
||||
$gridItemDesk: 175px;
|
||||
$gridItemMobile: 32px;
|
||||
/*************** Tabular */
|
||||
$tabularHeaderH: 22px;
|
||||
$tabularTdPadLR: $itemPadLR;
|
||||
$tabularTdPadTB: 2px;
|
||||
|
||||
/************************** MOBILE */
|
||||
$mobileMenuIconD: 34px; // Used
|
||||
$mobileMenuIconD: 24px; // Used
|
||||
$mobileTreeItemH: 35px; // Used
|
||||
|
||||
/************************** VISUAL */
|
||||
@ -121,7 +124,7 @@ $glyph-icon-pointer-right: '\e1028';
|
||||
$glyph-icon-refresh: '\e1029';
|
||||
$glyph-icon-save: '\e1030';
|
||||
$glyph-icon-sine: '\e1031';
|
||||
$glyph-icon-T: '\e1032';
|
||||
$glyph-icon-font: '\e1032';
|
||||
$glyph-icon-thumbs-strip: '\e1033';
|
||||
$glyph-icon-two-parts-both: '\e1034';
|
||||
$glyph-icon-two-parts-one-only: '\e1035';
|
||||
@ -138,7 +141,7 @@ $glyph-icon-frame-show: '\e1045';
|
||||
$glyph-icon-frame-hide: '\e1046';
|
||||
$glyph-icon-import: '\e1047';
|
||||
$glyph-icon-export: '\e1048';
|
||||
$glyph-icon-minimize: '\e1049'; // 12px only
|
||||
$glyph-icon-font-size: '\e1049';
|
||||
$glyph-icon-activity: '\e1100';
|
||||
$glyph-icon-activity-mode: '\e1101';
|
||||
$glyph-icon-autoflow-tabular: '\e1102';
|
||||
|
@ -20,115 +20,6 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/******************************************************** PLACEHOLDERS */
|
||||
@mixin cControl() {
|
||||
$fs: 1em;
|
||||
@include userSelectNone();
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
font-family: symbolsfont;
|
||||
display: block;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
&:after {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
[class*="__label"] {
|
||||
@include ellipsize();
|
||||
display: block;
|
||||
line-height: $fs; // Remove effect on top and bottom padding
|
||||
font-size: $fs;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin cButton() {
|
||||
@include cControl();
|
||||
background: $colorBtnBg;
|
||||
border-radius: $controlCr;
|
||||
color: $colorBtnFg;
|
||||
cursor: pointer;
|
||||
padding: nth($btnPad, 1) nth($btnPad, 2);
|
||||
|
||||
&:hover {
|
||||
background: $colorBtnBgHov;
|
||||
color: $colorBtnFgHov;
|
||||
}
|
||||
|
||||
&[class*="--major"] {
|
||||
background: $colorBtnMajorBg;
|
||||
color: $colorBtnMajorFg;
|
||||
|
||||
&:hover {
|
||||
background: $colorBtnMajorBgHov;
|
||||
color: $colorBtnMajorFgHov;
|
||||
}
|
||||
}
|
||||
|
||||
&[class*='--caution'] {
|
||||
background: $colorBtnCautionBg;
|
||||
color: $colorBtnCautionFg;
|
||||
|
||||
&:hover {
|
||||
background: $colorBtnCautionBgHov;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@mixin cClickIcon() {
|
||||
// A clickable element that just includes the icon, no background
|
||||
// Padding is included to facilitate a bigger hit area
|
||||
// Make the icon bigger relative to its container
|
||||
@include cControl();
|
||||
$pLR: 3px;
|
||||
$pTB: 3px;
|
||||
border-radius: $controlCr;
|
||||
color: $colorKey;
|
||||
cursor: pointer;
|
||||
padding: $pTB $pLR ;
|
||||
|
||||
&:hover {
|
||||
background: rgba($colorKey, 0.2);
|
||||
}
|
||||
|
||||
&:before,
|
||||
*:before {
|
||||
// *:before handles any nested containers that may contain glyph elements
|
||||
// Needed for c-togglebutton.
|
||||
font-size: 1.3em;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin cCtrlWrapper {
|
||||
// Provides a wrapper around buttons and other controls
|
||||
// Contains control and provides positioning context for contained menu/palette.
|
||||
// Wraps --menu elements, contains button and menu
|
||||
overflow: visible;
|
||||
|
||||
.c-menu {
|
||||
// Default position of contained menu
|
||||
top: 100%; left: 0;
|
||||
}
|
||||
|
||||
&[class*='--menus-up'] {
|
||||
.c-menu {
|
||||
top: auto; bottom: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
&[class*='--menus-left'] {
|
||||
.c-menu {
|
||||
left: auto; right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/********* Buttons */
|
||||
// Optionally can include icon in :before via markup
|
||||
button {
|
||||
@ -206,7 +97,7 @@ button {
|
||||
font-size: 0.7em;
|
||||
}
|
||||
}
|
||||
/********* Disclosure Triangle */
|
||||
/********* Disclosure Triangle */
|
||||
// Provides an arrow icon that when clicked expands an element to reveal its contents.
|
||||
// Used in tree items. Always placed BEFORE an element.
|
||||
.c-disclosure-triangle {
|
||||
@ -253,6 +144,12 @@ input[type=number] {
|
||||
}
|
||||
}
|
||||
|
||||
input[type=number]::-webkit-inner-spin-button,
|
||||
input[type=number]::-webkit-outer-spin-button {
|
||||
margin-right: -5px !important;
|
||||
margin-top: -1px !important;
|
||||
}
|
||||
|
||||
.c-input {
|
||||
&--datetime {
|
||||
// Sized for values such as 2018-09-28 22:32:33.468Z
|
||||
@ -266,8 +163,8 @@ input[type=number] {
|
||||
|
||||
&-inline,
|
||||
&--inline {
|
||||
// A text input or contenteditable element that indicates edit affordance on hover and looks like an input on focus
|
||||
@include reactive-input();
|
||||
// A text input or contenteditable element that indicates edit affordance on hover and looks like an input on focus
|
||||
@include reactive-input($bg: transparent);
|
||||
box-shadow: none;
|
||||
display: block !important;
|
||||
min-width: 0;
|
||||
@ -283,10 +180,11 @@ input[type=number] {
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background: $colorInputBg;
|
||||
padding-left: $inputTextPLeftRight;
|
||||
padding-right: $inputTextPLeftRight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&--labeled {
|
||||
// TODO: replace .c-labeled-input with this
|
||||
@ -297,7 +195,7 @@ input[type=number] {
|
||||
input {
|
||||
margin-left: $interiorMarginSm;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.c-labeled-input {
|
||||
@ -334,7 +232,6 @@ input[type=number] {
|
||||
}
|
||||
|
||||
@mixin menuInner() {
|
||||
color: $colorMenuFg;
|
||||
li {
|
||||
@include cControl();
|
||||
justify-content: start;
|
||||
@ -404,11 +301,6 @@ input[type=number] {
|
||||
justify-content: stretch;
|
||||
|
||||
.l-item-description {
|
||||
&__icon,
|
||||
&__description {
|
||||
//flex: 1 1 50%;
|
||||
}
|
||||
|
||||
&__name,
|
||||
&__description {
|
||||
margin-top: $interiorMarginLg;
|
||||
@ -420,6 +312,7 @@ input[type=number] {
|
||||
}
|
||||
|
||||
&__name {
|
||||
color: $colorMenuFg;
|
||||
flex: 0 0 auto;
|
||||
font-size: 1.25em;
|
||||
}
|
||||
|
@ -92,23 +92,16 @@ body.desktop {
|
||||
}
|
||||
}
|
||||
|
||||
.overlay ::-webkit-scrollbar-thumb {
|
||||
background: $scrollbarThumbColorOverlay;
|
||||
&:hover {
|
||||
background: $scrollbarThumbColorOverlayHov;
|
||||
}
|
||||
::-webkit-scrollbar-corner {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.menu ::-webkit-scrollbar-thumb {
|
||||
.c-menu ::-webkit-scrollbar-thumb {
|
||||
background: $scrollbarThumbColorMenu;
|
||||
&:hover {
|
||||
background: $scrollbarThumbColorMenuHov;
|
||||
}
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-corner {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
/************************** HTML ENTITIES */
|
||||
@ -175,7 +168,6 @@ li {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************** HAS */
|
||||
// Local Controls: Controls placed in proximity to or overlaid on components and views
|
||||
body.desktop .has-local-controls {
|
||||
@ -386,3 +378,8 @@ a.disabled {
|
||||
.t-imagery {
|
||||
display: contents;
|
||||
}
|
||||
|
||||
.t-frame-outer {
|
||||
min-width: 200px;
|
||||
min-height: 200px;
|
||||
}
|
||||
|
@ -113,7 +113,7 @@
|
||||
.icon-refresh { @include glyphBefore($glyph-icon-refresh); }
|
||||
.icon-save { @include glyphBefore($glyph-icon-save); }
|
||||
.icon-sine { @include glyphBefore($glyph-icon-sine); }
|
||||
.icon-T { @include glyphBefore($glyph-icon-T); }
|
||||
.icon-font { @include glyphBefore($glyph-icon-font); }
|
||||
.icon-thumbs-strip { @include glyphBefore($glyph-icon-thumbs-strip); }
|
||||
.icon-two-parts-both { @include glyphBefore($glyph-icon-two-parts-both); }
|
||||
.icon-two-parts-one-only { @include glyphBefore($glyph-icon-two-parts-one-only); }
|
||||
@ -130,6 +130,7 @@
|
||||
.icon-frame-hide { @include glyphBefore($glyph-icon-frame-hide); }
|
||||
.icon-import { @include glyphBefore($glyph-icon-import); }
|
||||
.icon-export { @include glyphBefore($glyph-icon-export); }
|
||||
.icon-font-size { @include glyphBefore($glyph-icon-font-size); }
|
||||
.icon-activity { @include glyphBefore($glyph-icon-activity); }
|
||||
.icon-activity-mode { @include glyphBefore($glyph-icon-activity-mode); }
|
||||
.icon-autoflow-tabular { @include glyphBefore($glyph-icon-autoflow-tabular); }
|
||||
|
@ -113,33 +113,14 @@
|
||||
top: $m; right: $m; bottom: $m; left: $m;
|
||||
}
|
||||
|
||||
@mixin gridTwoColumn() {
|
||||
display: grid;
|
||||
grid-row-gap: 0;
|
||||
grid-template-columns: 1fr 2fr;
|
||||
align-items: start;
|
||||
|
||||
[class*="header"] {
|
||||
@mixin propertiesHeader() {
|
||||
border-radius: $smallCr;
|
||||
background-color: $colorInspectorSectionHeaderBg;
|
||||
color: $colorInspectorSectionHeaderFg;
|
||||
font-weight: normal;
|
||||
margin: 0 0 $interiorMarginSm 0;
|
||||
padding: $interiorMarginSm $interiorMargin;
|
||||
|
||||
&:not(:first-child) {
|
||||
// Allow multiple headers within a component
|
||||
margin-top: $interiorMarginLg;
|
||||
}
|
||||
}
|
||||
|
||||
[class*="span-all"],
|
||||
[class*="header"] {
|
||||
@include gridTwoColumnSpanCols();
|
||||
}
|
||||
}
|
||||
|
||||
@mixin gridTwoColumnSpanCols() {
|
||||
grid-column: 1 / 3;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
@mixin modalFullScreen() {
|
||||
@ -163,10 +144,17 @@
|
||||
}
|
||||
|
||||
/************************** CONTROLS, BUTTONS */
|
||||
@mixin hover {
|
||||
body.desktop & {
|
||||
&:hover {
|
||||
@content
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@mixin htmlInputReset() {
|
||||
appearance: none;
|
||||
background: none;
|
||||
background-color: transparent;
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
outline: none;
|
||||
@ -177,13 +165,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@mixin input-base() {
|
||||
@include htmlInputReset();
|
||||
border-radius: $controlCr;
|
||||
|
||||
&.error {
|
||||
background-color: $colorFormFieldErrorBg;
|
||||
background: $colorFormFieldErrorBg;
|
||||
color: $colorFormFieldErrorFg;
|
||||
}
|
||||
}
|
||||
@ -201,22 +188,145 @@
|
||||
box-shadow: $shdwInput;
|
||||
color: $fg;
|
||||
|
||||
&:hover {
|
||||
box-shadow: $shdwInputHov;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
box-shadow: $shdwInputFoc;
|
||||
}
|
||||
|
||||
@include hover() {
|
||||
&:not(:focus) {
|
||||
box-shadow: $shdwInputHov;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@mixin button($bg: $colorBtnBg, $fg: $colorBtnFg, $radius: $controlCr, $shdw: none) {
|
||||
// Is this being used? Remove if not.
|
||||
background: $bg;
|
||||
color: $fg;
|
||||
border-radius: $radius;
|
||||
box-shadow: $shdw;
|
||||
}
|
||||
|
||||
@mixin cControl() {
|
||||
$fs: 1em;
|
||||
@include userSelectNone();
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
font-family: symbolsfont;
|
||||
display: block;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
&:after {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
[class*="__label"] {
|
||||
@include ellipsize();
|
||||
display: block;
|
||||
line-height: $fs; // Remove effect on top and bottom padding
|
||||
font-size: $fs;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin cButton() {
|
||||
@include cControl();
|
||||
@include themedButton();
|
||||
border-radius: $controlCr;
|
||||
color: $colorBtnFg;
|
||||
cursor: pointer;
|
||||
padding: $interiorMargin floor($interiorMargin * 1.25);
|
||||
|
||||
@include hover() {
|
||||
background: $colorBtnBgHov;
|
||||
color: $colorBtnFgHov;
|
||||
}
|
||||
|
||||
&[class*="--major"] {
|
||||
background: $colorBtnMajorBg;
|
||||
color: $colorBtnMajorFg;
|
||||
|
||||
@include hover() {
|
||||
background: $colorBtnMajorBgHov;
|
||||
color: $colorBtnMajorFgHov;
|
||||
}
|
||||
}
|
||||
|
||||
&[class*='--caution'] {
|
||||
background: $colorBtnCautionBg;
|
||||
color: $colorBtnCautionFg;
|
||||
|
||||
&:hover {
|
||||
background: $colorBtnCautionBgHov;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@mixin cClickIcon() {
|
||||
// A clickable element that just includes the icon, no background
|
||||
// Padding is included to facilitate a bigger hit area
|
||||
// Make the icon bigger relative to its container
|
||||
@include cControl();
|
||||
$pLR: 4px;
|
||||
$pTB: 3px;
|
||||
background: none;
|
||||
box-shadow: none;
|
||||
border-radius: $controlCr;
|
||||
color: $colorKey;
|
||||
cursor: pointer;
|
||||
padding: $pTB $pLR ;
|
||||
|
||||
@include hover() {
|
||||
background: $colorClickIconBgHov;
|
||||
color: $colorClickIconFgHov;
|
||||
}
|
||||
|
||||
&:before,
|
||||
*:before {
|
||||
// *:before handles any nested containers that may contain glyph elements
|
||||
// Needed for c-togglebutton.
|
||||
font-size: 1.2em;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin cCtrlWrapper {
|
||||
// Provides a wrapper around buttons and other controls
|
||||
// Contains control and provides positioning context for contained menu/palette.
|
||||
// Wraps --menu elements, contains button and menu
|
||||
overflow: visible;
|
||||
|
||||
.c-menu {
|
||||
// Default position of contained menu
|
||||
top: 100%; left: 0;
|
||||
}
|
||||
|
||||
&[class*='--menus-up'] {
|
||||
.c-menu {
|
||||
top: auto; bottom: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
&[class*='--menus-left'] {
|
||||
.c-menu {
|
||||
left: auto; right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@mixin wrappedInput() {
|
||||
// An input that is wrapped. Optionally includes a __label or icon element.
|
||||
// Based on .c-search.
|
||||
|
@ -2,7 +2,7 @@
|
||||
"metadata": {
|
||||
"name": "openmct-symbols-16px",
|
||||
"lastOpened": 0,
|
||||
"created": 1529545133464
|
||||
"created": 1537817705550
|
||||
},
|
||||
"iconSets": [
|
||||
{
|
||||
@ -525,7 +525,7 @@
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 102,
|
||||
"order": 149,
|
||||
"prevSize": 24,
|
||||
"name": "icon-T",
|
||||
"id": 84,
|
||||
@ -660,13 +660,21 @@
|
||||
"code": 921672,
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 150,
|
||||
"id": 119,
|
||||
"name": "icon-font-size-alt1",
|
||||
"prevSize": 24,
|
||||
"code": 921673,
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 37,
|
||||
"prevSize": 24,
|
||||
"name": "icon-activity",
|
||||
"id": 32,
|
||||
"code": 921856,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 36,
|
||||
@ -674,7 +682,7 @@
|
||||
"name": "icon-activity-mode",
|
||||
"id": 31,
|
||||
"code": 921857,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 52,
|
||||
@ -682,7 +690,7 @@
|
||||
"name": "icon-autoflow-tabular",
|
||||
"id": 47,
|
||||
"code": 921858,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 55,
|
||||
@ -690,7 +698,7 @@
|
||||
"name": "icon-clock",
|
||||
"id": 50,
|
||||
"code": 921859,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 58,
|
||||
@ -698,7 +706,7 @@
|
||||
"name": "icon-database",
|
||||
"id": 53,
|
||||
"code": 921860,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 57,
|
||||
@ -706,7 +714,7 @@
|
||||
"name": "icon-database-query",
|
||||
"id": 52,
|
||||
"code": 921861,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 17,
|
||||
@ -714,7 +722,7 @@
|
||||
"name": "icon-dataset",
|
||||
"id": 12,
|
||||
"code": 921862,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 22,
|
||||
@ -722,7 +730,7 @@
|
||||
"name": "icon-datatable",
|
||||
"id": 17,
|
||||
"code": 921863,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 59,
|
||||
@ -730,7 +738,7 @@
|
||||
"name": "icon-dictionary",
|
||||
"id": 54,
|
||||
"code": 921864,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 62,
|
||||
@ -738,7 +746,7 @@
|
||||
"name": "icon-folder",
|
||||
"id": 57,
|
||||
"code": 921865,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 66,
|
||||
@ -746,7 +754,7 @@
|
||||
"name": "icon-image",
|
||||
"id": 61,
|
||||
"code": 921872,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 68,
|
||||
@ -754,7 +762,7 @@
|
||||
"name": "icon-layout",
|
||||
"id": 63,
|
||||
"code": 921873,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 77,
|
||||
@ -762,7 +770,7 @@
|
||||
"name": "icon-object",
|
||||
"id": 72,
|
||||
"code": 921874,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 78,
|
||||
@ -770,7 +778,7 @@
|
||||
"name": "icon-object-unknown",
|
||||
"id": 73,
|
||||
"code": 921875,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 79,
|
||||
@ -778,7 +786,7 @@
|
||||
"name": "icon-packet",
|
||||
"id": 74,
|
||||
"code": 921876,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 80,
|
||||
@ -786,7 +794,7 @@
|
||||
"name": "icon-page",
|
||||
"id": 75,
|
||||
"code": 921877,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 135,
|
||||
@ -794,7 +802,7 @@
|
||||
"name": "icon-plot-overlay",
|
||||
"prevSize": 24,
|
||||
"code": 921878,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 113,
|
||||
@ -802,7 +810,7 @@
|
||||
"name": "icon-plot-stacked",
|
||||
"prevSize": 24,
|
||||
"code": 921879,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 10,
|
||||
@ -810,7 +818,7 @@
|
||||
"name": "icon-session",
|
||||
"id": 5,
|
||||
"code": 921880,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 24,
|
||||
@ -818,7 +826,7 @@
|
||||
"name": "icon-tabular",
|
||||
"id": 19,
|
||||
"code": 921881,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 7,
|
||||
@ -826,7 +834,7 @@
|
||||
"name": "icon-tabular-lad",
|
||||
"id": 2,
|
||||
"code": 921888,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 6,
|
||||
@ -834,7 +842,7 @@
|
||||
"name": "icon-tabular-lad-set",
|
||||
"id": 1,
|
||||
"code": 921889,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 8,
|
||||
@ -842,7 +850,7 @@
|
||||
"name": "icon-tabular-realtime",
|
||||
"id": 3,
|
||||
"code": 921890,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 23,
|
||||
@ -850,7 +858,7 @@
|
||||
"name": "icon-tabular-scrolling",
|
||||
"id": 18,
|
||||
"code": 921891,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 112,
|
||||
@ -858,7 +866,7 @@
|
||||
"name": "icon-telemetry",
|
||||
"id": 86,
|
||||
"code": 921892,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 90,
|
||||
@ -866,7 +874,7 @@
|
||||
"name": "icon-telemetry-panel",
|
||||
"id": 85,
|
||||
"code": 921893,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 93,
|
||||
@ -874,15 +882,15 @@
|
||||
"name": "icon-timeline",
|
||||
"id": 88,
|
||||
"code": 921894,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 116,
|
||||
"id": 101,
|
||||
"name": "icon-timer-v1.5",
|
||||
"name": "icon-timer-v15",
|
||||
"prevSize": 24,
|
||||
"code": 921895,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 11,
|
||||
@ -890,7 +898,7 @@
|
||||
"name": "icon-topic",
|
||||
"id": 6,
|
||||
"code": 921896,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 115,
|
||||
@ -898,7 +906,7 @@
|
||||
"name": "icon-box-with-dashed-lines",
|
||||
"id": 29,
|
||||
"code": 921897,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 126,
|
||||
@ -906,7 +914,7 @@
|
||||
"name": "icon-summary-widget",
|
||||
"prevSize": 24,
|
||||
"code": 921904,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 139,
|
||||
@ -914,13 +922,13 @@
|
||||
"name": "icon-notebook",
|
||||
"prevSize": 24,
|
||||
"code": 921905,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"name": "openmct-symbols-16px",
|
||||
"importSize": {
|
||||
"width": 512,
|
||||
"width": 745,
|
||||
"height": 512
|
||||
},
|
||||
"designer": "Charles Hacskaylo",
|
||||
@ -2360,7 +2368,7 @@
|
||||
},
|
||||
{
|
||||
"paths": [
|
||||
"M0 0v256h128v-64h256v704h-192v128h640v-128h-192v-704h256v64h128v-256z"
|
||||
"M800 1024h224l-384-1024h-256l-384 1024h224l84-224h408zM380 608l132-352 132 352z"
|
||||
],
|
||||
"grid": 16,
|
||||
"tags": [
|
||||
@ -2368,9 +2376,15 @@
|
||||
],
|
||||
"defaultCode": 228,
|
||||
"id": 84,
|
||||
"attrs": [],
|
||||
"attrs": [
|
||||
{}
|
||||
],
|
||||
"isMulticolor": false,
|
||||
"isMulticolor2": false,
|
||||
"colorPermutations": {
|
||||
"1161751207457516161751": []
|
||||
"1161751207457516161751": [
|
||||
{}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -2840,6 +2854,30 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 119,
|
||||
"paths": [
|
||||
"M1226.4 320h-176l-76.22 203.24 77 205.34 87.22-232.58 90.74 242h-174.44l49.5 132h174.44l57.76 154h154l-264-704z",
|
||||
"M384 0l-384 1024h224l84-224h408l84 224h224l-384-1024zM380 608l132-352 132 352z"
|
||||
],
|
||||
"attrs": [
|
||||
{},
|
||||
{}
|
||||
],
|
||||
"width": 1490,
|
||||
"isMulticolor": false,
|
||||
"isMulticolor2": false,
|
||||
"grid": 16,
|
||||
"tags": [
|
||||
"icon-font-size-alt1"
|
||||
],
|
||||
"colorPermutations": {
|
||||
"1161751207457516161751": [
|
||||
{},
|
||||
{}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"paths": [
|
||||
"M576 64h-256l320 320h-290.256c-44.264-76.516-126.99-128-221.744-128h-128v512h128c94.754 0 177.48-51.484 221.744-128h290.256l-320 320h256l448-448-448-448z"
|
||||
@ -3740,7 +3778,9 @@
|
||||
"classSelector": ".ui-symbol",
|
||||
"showMetrics": true,
|
||||
"showMetadata": true,
|
||||
"embed": false
|
||||
"embed": false,
|
||||
"noie8": true,
|
||||
"ie7": false
|
||||
},
|
||||
"imagePref": {
|
||||
"prefix": "icon-",
|
||||
|
@ -71,7 +71,7 @@
|
||||
<glyph unicode="󡀩" glyph-name="icon-refresh" d="M960 528v432l-164.8-164.8c-79.8 65.2-178.8 100.8-283.2 100.8-119.6 0-232.2-46.6-316.8-131.2s-131.2-197.2-131.2-316.8 46.6-232.2 131.2-316.8c84.6-84.6 197.2-131.2 316.8-131.2s232.2 46.6 316.8 131.2c69.4 69.4 113.2 157.4 126.6 252.8h-130c-29.8-145.8-159-256-313.6-256-176.4 0-320 143.6-320 320s143.8 320 320.2 320c72 0 138.4-23.8 192-64l-176-176h432z" />
|
||||
<glyph unicode="󡀰" glyph-name="icon-save" d="M192.2 384c-0.2 0-0.2 0 0 0l-0.2-448h640v447.8c0 0 0 0-0.2 0.2h-639.6zM978.8 749.2l-165.4 165.4c-25 25-74.2 45.4-109.4 45.4h-576c-70.4 0-128-57.6-128-128v-768c0-70.4 57.6-128 128-128v448c0 35.2 28.8 64 64 64h640c35.2 0 64-28.8 64-64v-448c70.4 0 128 57.6 128 128v576c0 35.2-20.4 84.4-45.2 109.2zM704 704c0-35.2-28.8-64-64-64h-448c-35.2 0-64 28.8-64 64v192h320v-192h128v192h128v-192z" />
|
||||
<glyph unicode="󡀱" glyph-name="icon-sine" d="M1022.294 448c-1.746 7.196-3.476 14.452-5.186 21.786-20.036 85.992-53.302 208.976-98 306.538-22.42 48.938-45.298 86.556-69.946 115.006-48.454 55.93-98.176 67.67-131.356 67.67s-82.902-11.74-131.356-67.672c-24.648-28.45-47.528-66.068-69.948-115.006-44.696-97.558-77.962-220.544-98-306.538-21.646-92.898-46.444-175.138-71.71-237.836-16.308-40.46-30.222-66.358-40.6-82.604-10.378 16.246-24.292 42.142-40.6 82.604-23.272 57.75-46.144 132.088-66.524 216.052h-197.362c1.746-7.196 3.476-14.452 5.186-21.786 20.036-85.992 53.302-208.976 98-306.538 22.42-48.938 45.298-86.556 69.946-115.006 48.454-55.932 98.176-67.672 131.356-67.672s82.902 11.74 131.356 67.672c24.648 28.45 47.528 66.068 69.948 115.006 44.696 97.558 77.962 220.544 98 306.538 21.646 92.898 46.444 175.138 71.71 237.836 16.308 40.46 30.222 66.358 40.6 82.604 10.378-16.246 24.292-42.142 40.6-82.604 23.274-57.748 46.146-132.086 66.526-216.050h197.36z" />
|
||||
<glyph unicode="󡀲" glyph-name="icon-T" d="M0 960v-256h128v64h256v-704h-192v-128h640v128h-192v704h256v-64h128v256z" />
|
||||
<glyph unicode="󡀲" glyph-name="icon-T" d="M800-64h224l-384 1024h-256l-384-1024h224l84 224h408zM380 352l132 352 132-352z" />
|
||||
<glyph unicode="󡀳" glyph-name="icon-thumbs-strip" d="M448 578c0-35.2-28.8-64-64-64h-320c-35.2 0-64 28.8-64 64v320c0 35.2 28.8 64 64 64h320c35.2 0 64-28.8 64-64v-320zM1024 578c0-35.2-28.8-64-64-64h-320c-35.2 0-64 28.8-64 64v320c0 35.2 28.8 64 64 64h320c35.2 0 64-28.8 64-64v-320zM448 2c0-35.2-28.8-64-64-64h-320c-35.2 0-64 28.8-64 64v320c0 35.2 28.8 64 64 64h320c35.2 0 64-28.8 64-64v-320zM1024 2c0-35.2-28.8-64-64-64h-320c-35.2 0-64 28.8-64 64v320c0 35.2 28.8 64 64 64h320c35.2 0 64-28.8 64-64v-320z" />
|
||||
<glyph unicode="󡀴" glyph-name="icon-two-parts-both" d="M896 960h-768c-70.4 0-128-57.6-128-128v-768c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v768c0 70.4-57.6 128-128 128zM128 832h320v-768h-320v768zM896 64h-320v768h320v-768z" />
|
||||
<glyph unicode="󡀵" glyph-name="icon-two-parts-one-only" d="M896 960h-768c-70.4 0-128-57.6-128-128v-768c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v768c0 70.4-57.6 128-128 128zM896 64h-320v768h320v-768z" />
|
||||
@ -88,6 +88,7 @@
|
||||
<glyph unicode="󡁆" glyph-name="icon-frame-hide" d="M128 770h420l104 128h-652v-802.4l128 157.4zM896 130h-420l-104-128h652v802.4l-128-157.4zM832 962l-832-1024h192l832 1024zM392 578l104 128h-304v-128z" />
|
||||
<glyph unicode="󡁇" glyph-name="icon-import" d="M832 767.6v-639.4c0-0.2-0.2-0.2-0.4-0.4h-319.6v-192h320c105.6 0 192 86.4 192 192v640.2c0 105.6-86.4 192-192 192h-320v-192h319.6c0.2 0 0.4-0.2 0.4-0.4zM192 256v-192l384 384-384 384v-192h-192v-384z" />
|
||||
<glyph unicode="󡁈" glyph-name="icon-export" d="M192 128.34v639.32l0.34 0.34h319.66v192h-320c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h320v192h-319.66zM1024 448l-384 384v-192h-192v-384h192v-192l384 384z" />
|
||||
<glyph unicode="󡁉" glyph-name="icon-font-size-alt1" horiz-adv-x="1490" d="M1226.4 640h-176l-76.22-203.24 77-205.34 87.22 232.58 90.74-242h-174.44l49.5-132h174.44l57.76-154h154l-264 704zM384 960l-384-1024h224l84 224h408l84-224h224l-384 1024zM380 352l132 352 132-352z" />
|
||||
<glyph unicode="󡄀" glyph-name="icon-activity" d="M576 896h-256l320-320h-290.256c-44.264 76.516-126.99 128-221.744 128h-128v-512h128c94.754 0 177.48 51.484 221.744 128h290.256l-320-320h256l448 448-448 448z" />
|
||||
<glyph unicode="󡄁" glyph-name="icon-activity-mode" d="M512 960c-214.866 0-398.786-132.372-474.744-320h90.744c56.86 0 107.938-24.724 143.094-64h240.906l-192 192h256l320-320-320-320h-256l192 192h-240.906c-35.156-39.276-86.234-64-143.094-64h-90.744c75.958-187.628 259.878-320 474.744-320 282.77 0 512 229.23 512 512s-229.23 512-512 512z" />
|
||||
<glyph unicode="󡄂" glyph-name="icon-autoflow-tabular" d="M192 960c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h64v1024h-64zM384 960h256v-1024h-256v1024zM832 960h-64v-704h256v512c0 105.6-86.4 192-192 192z" />
|
||||
@ -115,7 +116,7 @@
|
||||
<glyph unicode="󡄤" glyph-name="icon-telemetry" d="M32 328.34c14.28 5.62 54.44 47.54 92.96 146 42.46 108.38 116.32 237.66 227.040 237.66 52.4 0 101.42-29.16 145.7-86.68 37.34-48.5 64.84-108.92 81.34-151.080 38.52-98.38 78.68-140.3 92.96-146 14.28 5.62 54.44 47.54 92.96 146 37.4 95.5 99.14 207.14 188.94 232.46-90.462 152.598-254.314 253.3-441.686 253.3-0.075 0-0.15 0-0.226 0-282.748 0-511.988-229.24-511.988-512 0-0.032 0-0.070 0-0.108 0-35.719 3.641-70.587 10.572-104.255 8.968-7.457 16.648-13.417 21.428-15.297zM992 567.66c-14.28-5.62-54.44-47.52-92.96-146-42.46-108.38-116.32-237.66-227.040-237.66-52.4 0-101.42 29.16-145.7 86.68-37.34 48.5-64.84 108.92-81.34 151.080-38.52 98.38-78.68 140.3-92.96 146-14.28-5.62-54.44-47.52-92.96-146-37.4-95.5-99.14-207.14-188.94-232.46 90.462-152.598 254.314-253.3 441.686-253.3 0.075 0 0.15 0 0.226 0 282.748 0 511.988 229.24 511.988 512 0 0.032 0 0.070 0 0.108 0 35.719-3.641 70.587-10.572 104.255-8.968 7.457-16.648 13.417-21.428 15.297z" />
|
||||
<glyph unicode="󡄥" glyph-name="icon-telemetry-panel" d="M169.2 512c14 56.4 33 122 56.6 176.8 15.4 35.8 31.2 63.2 48.2 84 18.4 22.4 49 49.2 91 49.2s72.6-26.8 91-49.2c17-20.6 32.6-48.2 48.2-84 23.6-54.8 42.8-120.4 56.6-176.8h461.2v256c0 105.6-86.4 192-192 192h-640c-105.6 0-192-86.4-192-192v-256h171.2zM718.6 384h-127.2c25-93.4 48.4-144.4 63.6-168.6 15.2 24.2 38.6 75.2 63.6 168.6zM301.4 512h127.2c-25 93.4-48.4 144.4-63.6 168.6-15.2-24.2-38.6-75.2-63.6-168.6zM850.8 384c-14-56.4-33-122-56.6-176.8-15.4-35.8-31.2-63.2-48.2-84-18.4-22.4-49-49.2-91-49.2s-72.6 26.8-91 49.2c-17 20.6-32.6 48.2-48.2 84-23.6 54.8-42.8 120.4-56.6 176.8h-461.2v-256c0-105.6 86.4-192 192-192h640c105.6 0 192 86.4 192 192v256h-171.2z" />
|
||||
<glyph unicode="󡄦" glyph-name="icon-timeline" d="M256 704h384v-128h-384v128zM384 512h384v-128h-384v128zM320 320h384v-128h-384v128zM832 960h-128v-192h127.6c0.2 0 0.2-0.2 0.4-0.4v-639.4c0-0.2-0.2-0.2-0.4-0.4h-127.6v-192h128c105.6 0 192 86.4 192 192v640.2c0 105.6-86.4 192-192 192zM192 128.4v639.2c0 0.2 0.2 0.2 0.4 0.4h127.6v192h-128c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h128v192h-127.6c-0.2 0-0.4 0.2-0.4 0.4z" />
|
||||
<glyph unicode="󡄧" glyph-name="icon-timer-v1.5" horiz-adv-x="896" d="M576 813.4v82.58c0 35.346-28.654 64-64 64h-128c-35.346 0-64-28.654-64-64v-82.58c-185.040-55.080-320-226.48-320-429.42 0-247.42 200.58-448 448-448s448 200.58 448 448c0 202.96-135 374.4-320 429.42zM468 363.98l-263.76-211c-57.105 59.935-92.24 141.251-92.24 230.772 0 0.080 0 0.16 0 0.24 0 185.268 150.72 335.988 336 335.988 6.72 0 13.38-0.22 20-0.62v-355.38z" />
|
||||
<glyph unicode="󡄧" glyph-name="icon-timer-v15" horiz-adv-x="896" d="M576 813.4v82.58c0 35.346-28.654 64-64 64h-128c-35.346 0-64-28.654-64-64v-82.58c-185.040-55.080-320-226.48-320-429.42 0-247.42 200.58-448 448-448s448 200.58 448 448c0 202.96-135 374.4-320 429.42zM468 363.98l-263.76-211c-57.105 59.935-92.24 141.251-92.24 230.772 0 0.080 0 0.16 0 0.24 0 185.268 150.72 335.988 336 335.988 6.72 0 13.38-0.22 20-0.62v-355.38z" />
|
||||
<glyph unicode="󡄨" glyph-name="icon-topic" d="M454.36 483.36l86.3 86.3c9.088 8.965 21.577 14.502 35.36 14.502s26.272-5.537 35.366-14.507l86.294-86.294c19.328-19.358 42.832-34.541 69.047-44.082l1.313 171.722-57.64 57.64c-34.407 34.33-81.9 55.558-134.35 55.558s-99.943-21.228-134.354-55.562l-86.296-86.297c-9.088-8.965-21.577-14.502-35.36-14.502s-26.272 5.537-35.366 14.507l-28.674 28.654v-172.14c19.045-7.022 41.040-11.084 63.984-11.084 52.463 0 99.966 21.239 134.379 55.587zM505.64 412.64l-86.3-86.3c-9.088-8.965-21.577-14.502-35.36-14.502s-26.272 5.537-35.366 14.507l-86.294 86.294c-2 2-4.2 4-6.36 6v-197.36c33.664-30.72 78.65-49.537 128.031-49.537 52.44 0 99.923 21.22 134.333 55.541l86.296 86.296c9.088 8.965 21.577 14.502 35.36 14.502s26.272-5.537 35.366-14.507l86.294-86.294c2-2 4.2-4 6.36-6v197.36c-33.664 30.72-78.65 49.537-128.031 49.537-52.44 0-99.923-21.22-134.333-55.541zM832 960h-128v-192h127.66l0.34-0.34v-639.32l-0.34-0.34h-127.66v-192h128c105.6 0 192 86.4 192 192v640c0 105.6-86.4 192-192 192zM320 128h-127.66l-0.34 0.34v639.32l0.34 0.34h127.66v192h-128c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h128v192z" />
|
||||
<glyph unicode="󡄩" glyph-name="icon-box-with-dashed-lines" d="M0 576h128v-256h-128v256zM128 831.78l0.22 0.22h191.78v128h-192c-70.606-0.215-127.785-57.394-128-127.979v-192.021h128v191.78zM128 64.22v191.78h-128v-192c0.215-70.606 57.394-127.785 127.979-128h192.021v128h-191.78zM384 960h256v-128h-256v128zM896 64.22l-0.22-0.22h-191.78v-128h192c70.606 0.215 127.785 57.394 128 127.979v192.021h-128v-191.78zM896 960h-192v-128h191.78l0.22-0.22v-191.78h128v192c-0.215 70.606-57.394 127.785-127.979 128zM896 576h128v-256h-128v256zM384 64h256v-128h-256v128zM256 704h512v-512h-512v512z" />
|
||||
<glyph unicode="󡄰" glyph-name="icon-summary-widget" d="M896 960h-768c-70.4 0-128-57.6-128-128v-768c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v768c0 70.4-57.6 128-128 128zM847.8 349.6l-82.6-143.2-189.6 131.6 19.2-230h-165.4l19.2 230-189.6-131.6-82.6 143.2 208.6 98.4-208.8 98.4 82.6 143.2 189.6-131.6-19.2 230h165.4l-19.2-230 189.6 131.6 82.6-143.2-208.6-98.4 208.8-98.4z" />
|
||||
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
Binary file not shown.
Binary file not shown.
@ -24,8 +24,8 @@
|
||||
//!********************************* CONTROLS *!
|
||||
//@import "../styles/controls/breadcrumb";
|
||||
@import "../styles/controls/buttons";
|
||||
@import "../styles/controls/palette";
|
||||
@import "../styles/controls/controls";
|
||||
//@import "../styles/controls/palette";
|
||||
//@import "../styles/controls/controls";
|
||||
@import "../styles/controls/lists";
|
||||
@import "../styles/controls/menus";
|
||||
@import "../styles/controls/messages";
|
||||
@ -46,7 +46,7 @@
|
||||
//@import "../styles/search/search";
|
||||
//@import "../styles/mobile/search/search";
|
||||
@import "../styles/overlay/overlay";
|
||||
//@import "../styles/tree/tree";
|
||||
@import "../styles/tree/tree"; // TEMP - NEED FOR TREE IN INSPECTOR
|
||||
@import "../styles/object-label";
|
||||
//@import "../styles/mobile/tree";
|
||||
@import "../styles/user-environ/frame";
|
||||
|
@ -116,7 +116,6 @@
|
||||
$p: $interiorMarginSm;
|
||||
@include discreteItem();
|
||||
display: flex;
|
||||
//flex-wrap: wrap;
|
||||
padding: $interiorMarginSm $interiorMarginSm $interiorMarginSm $interiorMargin;
|
||||
|
||||
&__time,
|
||||
@ -156,7 +155,7 @@
|
||||
}
|
||||
|
||||
&__text {
|
||||
min-height: 24px; // Needed in Firefox when field is blank
|
||||
min-height: 22px; // Needed in Firefox when field is blank
|
||||
white-space: pre-wrap;
|
||||
|
||||
&.is-blank-notebook-entry {
|
||||
|
@ -24,5 +24,6 @@
|
||||
// Meant for use as a single line import in Vue SFC's.
|
||||
// Do not include anything that renders to CSS!
|
||||
@import "constants";
|
||||
@import "constants-snow"; // TEMP
|
||||
@import "constants-espresso"; // TEMP
|
||||
//@import "constants-snow"; // TEMP
|
||||
@import "mixins";
|
@ -21,6 +21,26 @@
|
||||
*****************************************************************************/
|
||||
|
||||
/************************************************************* WIDGET OBJECT */
|
||||
@mixin cSummaryWidget() {
|
||||
@include boxShdw($shdwBtns);
|
||||
border-radius: $basicCr;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
cursor: default;
|
||||
&[href] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&__label {
|
||||
&:before {
|
||||
// Widget icon
|
||||
font-size: 0.9em;
|
||||
margin-right: $interiorMarginSm;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.l-summary-widget {
|
||||
// Widget layout classes here
|
||||
@include ellipsize();
|
||||
@ -33,19 +53,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
.s-summary-widget {
|
||||
// Widget style classes here
|
||||
@include boxShdw($shdwBtns);
|
||||
border-radius: $basicCr;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
box-sizing: border-box;
|
||||
cursor: default;
|
||||
font-size: 0.8rem;
|
||||
.c-summary-widget {
|
||||
@include cSummaryWidget();
|
||||
padding: $interiorMarginLg $interiorMarginLg * 2;
|
||||
&[href] {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.widget-edit-holder {
|
||||
@ -267,7 +277,6 @@
|
||||
|
||||
.widget-thumb {
|
||||
@include ellipsize();
|
||||
@extend .s-summary-widget;
|
||||
@extend .l-summary-widget;
|
||||
@include cSummaryWidget();
|
||||
padding: $interiorMarginSm $interiorMargin;
|
||||
}
|
@ -31,10 +31,6 @@
|
||||
|
||||
.c-create-button,
|
||||
.c-create-menu {
|
||||
&--w {
|
||||
// Wrapper for Create button and menu
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
168
src/ui/components/controls/checkboxCustom.vue
Normal file
168
src/ui/components/controls/checkboxCustom.vue
Normal file
@ -0,0 +1,168 @@
|
||||
<template>
|
||||
<div class="c-custom-checkbox">
|
||||
<input type="checkbox"
|
||||
:id="id"
|
||||
:name="name"
|
||||
:value="value"
|
||||
:required="required"
|
||||
:disabled="disabled"
|
||||
@change="onChange"
|
||||
:checked="state">
|
||||
<label :for="id">
|
||||
<div class="c-custom-checkbox__box"></div>
|
||||
<div class="c-custom-checkbox__label-text">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@import "~styles/sass-base";
|
||||
|
||||
.c-custom-checkbox {
|
||||
$d: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
label {
|
||||
@include userSelectNone();
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&__box {
|
||||
@include nice-input();
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
line-height: $d;
|
||||
width: $d;
|
||||
height: $d;
|
||||
margin-right: $interiorMarginSm;
|
||||
}
|
||||
|
||||
input {
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
|
||||
&:checked + label > .c-custom-checkbox__box {
|
||||
background: $colorKey;
|
||||
&:before {
|
||||
color: $colorKeyFg;
|
||||
content: $glyph-icon-check;
|
||||
font-family: symbolsfont;
|
||||
font-size: 0.6em;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(:disabled) + label {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&:disabled + label {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
/*
|
||||
Custom checkbox control. Use just like a checkbox in HTML, except label string is passed within tag.
|
||||
Supports value, true-value, false-value, checked and disabled attributes.
|
||||
Example usage:
|
||||
<checkbox checked>Enable markers</checkbox>
|
||||
*/
|
||||
export default {
|
||||
model: {
|
||||
prop: 'modelValue',
|
||||
event: 'input'
|
||||
},
|
||||
|
||||
props: {
|
||||
id: {
|
||||
type: String,
|
||||
default: function () {
|
||||
return 'checkbox-id-' + this._uid;
|
||||
},
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
value: {
|
||||
default: null,
|
||||
},
|
||||
modelValue: {
|
||||
default: undefined,
|
||||
},
|
||||
checked: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
required: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
model: {}
|
||||
},
|
||||
|
||||
computed: {
|
||||
state() {
|
||||
if (this.modelValue === undefined) {
|
||||
return this.checked;
|
||||
}
|
||||
|
||||
if (Array.isArray(this.modelValue)) {
|
||||
return this.modelValue.indexOf(this.value) > -1;
|
||||
}
|
||||
|
||||
return !!this.modelValue;
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
onChange() {
|
||||
this.toggle();
|
||||
},
|
||||
|
||||
toggle() {
|
||||
let value;
|
||||
|
||||
if (Array.isArray(this.modelValue)) {
|
||||
value = this.modelValue.slice(0);
|
||||
|
||||
if (this.state) {
|
||||
value.splice(value.indexOf(this.value), 1);
|
||||
} else {
|
||||
value.push(this.value);
|
||||
}
|
||||
} else {
|
||||
value = !this.state;
|
||||
}
|
||||
|
||||
this.$emit('input', value);
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
checked(newValue) {
|
||||
if (newValue !== this.state) {
|
||||
this.toggle();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
if (this.checked && !this.state) {
|
||||
this.toggle();
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
51
src/ui/components/controls/labeledNumberInput.vue
Normal file
51
src/ui/components/controls/labeledNumberInput.vue
Normal file
@ -0,0 +1,51 @@
|
||||
<template>
|
||||
<div class="c-labeled-input"
|
||||
:title="title">
|
||||
<div class="c-labeled-input__label">{{ label }}</div>
|
||||
<input type="number"
|
||||
v-bind="$attrs"
|
||||
v-bind:value="value"
|
||||
v-on="inputListeners"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/* Emits input and clear events */
|
||||
export default {
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
value: String,
|
||||
label: String,
|
||||
title: String
|
||||
},
|
||||
computed: {
|
||||
inputListeners: function () {
|
||||
let vm = this;
|
||||
return Object.assign({},
|
||||
this.$listeners,
|
||||
{
|
||||
input: function (event) {
|
||||
vm.$emit('input', event.target.value);
|
||||
},
|
||||
change: function (event) {
|
||||
vm.$emit('change', event.target.value);
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
},
|
||||
data: function() {
|
||||
return {
|
||||
// active: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
clearInput() {
|
||||
// Clear the user's input and set 'active' to false
|
||||
this.value = '';
|
||||
this.$emit('clear','');
|
||||
this.active = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
@ -5,7 +5,6 @@
|
||||
'l-pane--horizontal-handle-after': type === 'horizontal' && handle === 'after',
|
||||
'l-pane--vertical-handle-before': type === 'vertical' && handle === 'before',
|
||||
'l-pane--vertical-handle-after': type === 'vertical' && handle === 'after',
|
||||
'l-pane--collapsable' : collapsable,
|
||||
'l-pane--collapsed': collapsed,
|
||||
'l-pane--reacts': !handle,
|
||||
'l-pane--resizing': resizing === true
|
||||
@ -14,11 +13,13 @@
|
||||
class="l-pane__handle"
|
||||
@mousedown="start">
|
||||
</div>
|
||||
<button v-if="label"
|
||||
class="l-pane__collapse-button"
|
||||
@click="toggleCollapse">
|
||||
<div class="l-pane__header"
|
||||
v-if="label">
|
||||
<span class="l-pane__label">{{ label }}</span>
|
||||
</button>
|
||||
<button class="l-pane__collapse-button c-button"
|
||||
v-if="collapsable"
|
||||
@click="toggleCollapse"></button>
|
||||
</div>
|
||||
<div class="l-pane__contents">
|
||||
<slot></slot>
|
||||
</div>
|
||||
@ -62,13 +63,10 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
&__collapse-button {
|
||||
position: absolute;
|
||||
&__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
top: 0; right: 0; // Default
|
||||
z-index: 1;
|
||||
margin-bottom: $interiorMargin;
|
||||
}
|
||||
|
||||
&--reacts {
|
||||
@ -85,6 +83,7 @@
|
||||
transition: opacity 150ms ease;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
overflow: hidden;
|
||||
|
||||
> * {
|
||||
min-width: 0 !important;
|
||||
@ -111,7 +110,6 @@
|
||||
&__contents {
|
||||
flex: 1 1 100%;
|
||||
opacity: 1;
|
||||
padding: $interiorMargin;
|
||||
pointer-events: inherit;
|
||||
transition: opacity 250ms ease 250ms;
|
||||
|
||||
@ -121,13 +119,10 @@
|
||||
}
|
||||
|
||||
> [class*="__"] + [class*="__"] {
|
||||
// Create margin between elements in a pane
|
||||
// Doesn't match first elem, but will match all subsequent
|
||||
margin-top: $interiorMargin;
|
||||
}
|
||||
}
|
||||
|
||||
/************************ DESKTOP STYLES */
|
||||
/************************************************ DESKTOP STYLES */
|
||||
body.desktop & {
|
||||
&__handle {
|
||||
background: $colorSplitterBg;
|
||||
@ -150,52 +145,35 @@
|
||||
}
|
||||
}
|
||||
|
||||
&__collapse-button {
|
||||
$m: 2px;
|
||||
$h: 12px;
|
||||
color: $splitterBtnColorFg;
|
||||
flex: 0 0 nth($splitterBtnD, 1);
|
||||
font-size: $h * .9;
|
||||
position: relative;
|
||||
justify-content: start;
|
||||
transition: $transOut;
|
||||
&__header {
|
||||
font-size: floor(12px * .9);
|
||||
}
|
||||
|
||||
&:after {
|
||||
// Close icon
|
||||
background: $colorBtnBg;
|
||||
&__collapse-button {
|
||||
box-shadow: none;
|
||||
background: $splitterBtnColorBg;
|
||||
color: $splitterBtnColorFg;
|
||||
border-radius: $smallCr;
|
||||
color: $colorBtnFg;
|
||||
content: $glyph-icon-arrow-right-equilateral;
|
||||
display: block;
|
||||
font-family: symbolsfont;
|
||||
font-size: 6px;
|
||||
line-height: 90%;
|
||||
padding: 3px 15px;
|
||||
position: absolute;
|
||||
right: $m;
|
||||
top: $m;
|
||||
transition: $transOut;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: rgba(black, 0.1);
|
||||
&:after {
|
||||
background: $splitterBtnColorHoverBg;
|
||||
color: $splitterBtnColorHoverFg;
|
||||
transition: $transIn;
|
||||
}
|
||||
@include hover() {
|
||||
background: $colorBtnBgHov;
|
||||
color: $colorBtnFgHov;
|
||||
}
|
||||
}
|
||||
|
||||
&__label {
|
||||
// Name of the pane
|
||||
@include ellipsize();
|
||||
@include userSelectNone();
|
||||
color: $splitterBtnLabelColorFg;
|
||||
display: block;
|
||||
padding-right: nth($splitterBtnD, 2) + $interiorMargin; // Force label to ellipsis
|
||||
pointer-events: none;
|
||||
text-transform: uppercase;
|
||||
transform-origin: top left;
|
||||
flex: 1 0 90%;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
&--resizing {
|
||||
@ -208,6 +186,7 @@
|
||||
}
|
||||
|
||||
&[class*="--collapsed"] {
|
||||
/********************************* STYLES FOR DESKTOP COLLAPSED PANES, ALL ORIENTATIONS */
|
||||
$d: nth($splitterBtnD, 1);
|
||||
flex-basis: $d;
|
||||
min-width: $d !important;
|
||||
@ -217,19 +196,24 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
> .l-pane__collapse-button {
|
||||
background: $splitterBtnColorFg;
|
||||
color: $splitterBtnColorBg;
|
||||
|
||||
.l-pane__header {
|
||||
&:hover {
|
||||
background: $splitterBtnColorHoverBg !important;
|
||||
color: $splitterCollapsedBtnColorFgHov;
|
||||
.l-pane__label {
|
||||
color: inherit;
|
||||
}
|
||||
.l-pane__collapse-button {
|
||||
background: $splitterCollapsedBtnColorBgHov;
|
||||
color: inherit;
|
||||
transition: $transIn;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .l-pane__collapse-button {
|
||||
height: nth($splitterBtnD, 1);
|
||||
padding: $interiorMarginSm $interiorMarginSm;
|
||||
.l-pane__collapse-button {
|
||||
background: $splitterCollapsedBtnColorBg;
|
||||
color: $splitterCollapsedBtnColorFg;
|
||||
}
|
||||
}
|
||||
|
||||
&[class*="--horizontal"] {
|
||||
@ -248,31 +232,44 @@
|
||||
}
|
||||
}
|
||||
|
||||
.l-pane__collapse-button {
|
||||
&:before {
|
||||
content: $glyph-icon-arrow-right-equilateral;
|
||||
}
|
||||
}
|
||||
|
||||
&[class*="--collapsed"] {
|
||||
> .l-pane__collapse-button {
|
||||
position: absolute;
|
||||
top: 0; right: 0; bottom: 0; left: 0;
|
||||
height: auto; width: 100%;
|
||||
/************************ COLLAPSED HORIZONTAL SPLITTER, EITHER DIRECTION */
|
||||
[class*="__header"] {
|
||||
@include abs();
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
[class*="label"] {
|
||||
position: absolute;
|
||||
transform: translate($interiorMarginLg + 1, 18px) rotate(90deg);
|
||||
left: 3px;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&:after {
|
||||
background: none;
|
||||
.l-pane__collapse-button {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0; // Only have to do this once, because of scaleX(-1) below.
|
||||
position: absolute;
|
||||
top: 0; right: 0; bottom: 0; left: 0;
|
||||
height: auto; width: 100%;
|
||||
padding: 0;
|
||||
top: $interiorMargin;
|
||||
left: 50%;
|
||||
right: auto;
|
||||
transform: translateX(-50%);
|
||||
width: auto;
|
||||
|
||||
&:before {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/************************** Horizontal Splitter Before */
|
||||
// Inspector pane
|
||||
&[class*="-before"] {
|
||||
> .l-pane__handle {
|
||||
left: 0;
|
||||
@ -280,15 +277,14 @@
|
||||
}
|
||||
|
||||
&[class*="--collapsed"] {
|
||||
> .l-pane__collapse-button {
|
||||
&:after {
|
||||
transform: translateX(-50%) scaleX(-1);
|
||||
}
|
||||
.l-pane__collapse-button {
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/************************** Horizontal Splitter After */
|
||||
// Tree pane
|
||||
&[class*="-after"] {
|
||||
> .l-pane__handle {
|
||||
right: 0;
|
||||
@ -296,7 +292,7 @@
|
||||
}
|
||||
|
||||
&:not([class*="--collapsed"]) {
|
||||
> .l-pane__collapse-button:after {
|
||||
.l-pane__collapse-button {
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
}
|
||||
@ -320,19 +316,19 @@
|
||||
}
|
||||
|
||||
/************************** Vertical Splitter Before */
|
||||
// Pane collapses downward
|
||||
// Pane collapses downward. Used by Elements pool in Inspector
|
||||
&[class*="-before"] {
|
||||
> .l-pane__handle {
|
||||
top: 0;
|
||||
transform: translateY(floor($splitterHandleD / -1));
|
||||
}
|
||||
|
||||
> .l-pane__collapse-button:after {
|
||||
.l-pane__collapse-button:before {
|
||||
content: $glyph-icon-arrow-down;
|
||||
}
|
||||
|
||||
&.l-pane--collapsed {
|
||||
> .l-pane__collapse-button:after {
|
||||
> .l-pane__collapse-button {
|
||||
transform: scaleY(-1);
|
||||
}
|
||||
}
|
||||
|
@ -15,45 +15,15 @@
|
||||
<style lang="scss">
|
||||
@import "~styles/sass-base";
|
||||
|
||||
/******************************* SEARCH */
|
||||
.c-search {
|
||||
@include nice-input();
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 2px 4px;
|
||||
@include wrappedInput();
|
||||
|
||||
padding-top: 2px;
|
||||
padding-bottom: 2px;
|
||||
|
||||
&:before {
|
||||
// Mag glass icon
|
||||
content: $glyph-icon-magnify;
|
||||
direction: rtl; // Aligns glyph to right-hand side of container, for transition
|
||||
display: block;
|
||||
font-family: symbolsfont;
|
||||
flex: 0 0 auto;
|
||||
opacity: 0.5;
|
||||
overflow: hidden;
|
||||
padding: 2px 0; // Prevents clipping
|
||||
transition: width 250ms ease;
|
||||
width: 1em;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
box-shadow: inset rgba(black, 0.8) 0 0px 2px;
|
||||
&:before {
|
||||
opacity: 0.9;
|
||||
}
|
||||
}
|
||||
|
||||
&--major {
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
&__input {
|
||||
background: none !important;
|
||||
box-shadow: none !important; // !important needed to override default for [input]
|
||||
flex: 1 1 auto;
|
||||
padding-left: 2px !important;
|
||||
padding-right: 2px !important;
|
||||
min-width: 10px; // Must be set to allow input to collapse below browser min
|
||||
}
|
||||
|
||||
&__clear-input {
|
||||
@ -61,11 +31,6 @@
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
&:before {
|
||||
padding: 2px 0px;
|
||||
width: 0px;
|
||||
}
|
||||
|
||||
.c-search__clear-input {
|
||||
display: block;
|
||||
}
|
||||
|
176
src/ui/components/controls/toggleButton.vue
Normal file
176
src/ui/components/controls/toggleButton.vue
Normal file
@ -0,0 +1,176 @@
|
||||
<template>
|
||||
<div class="c-togglebutton">
|
||||
<input type="checkbox"
|
||||
:id="id"
|
||||
:name="name"
|
||||
:value="value"
|
||||
:required="required"
|
||||
:disabled="disabled"
|
||||
@change="onChange"
|
||||
:checked="state">
|
||||
<label :for="id">
|
||||
<div class="c-togglebutton__on"
|
||||
:class="innerClassOn"></div>
|
||||
<div class="c-togglebutton__off"
|
||||
:class="innerClassOff"></div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@import "~styles/sass-base";
|
||||
|
||||
.c-togglebutton {
|
||||
$d: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.c-togglebutton__on {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
input {
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
|
||||
&:checked + label {
|
||||
.c-togglebutton__on {
|
||||
display: block;
|
||||
}
|
||||
.c-togglebutton__off {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(:disabled) + label {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&:disabled + label {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
/*
|
||||
Toggle button control, based on checkboxCustom. Use just like a checkbox in HTML.
|
||||
Requires inner-class-on and -off attributes to be passed.
|
||||
Supports checked and disabled attributes.
|
||||
Example usage:
|
||||
<toggle-button checked
|
||||
class="c-click-icon"
|
||||
inner-class-on="icon-grid-snap-to"
|
||||
inner-class-off="icon-grid-snap-no"></toggle-button>
|
||||
*/
|
||||
export default {
|
||||
model: {
|
||||
prop: 'modelValue',
|
||||
event: 'input'
|
||||
},
|
||||
|
||||
props: {
|
||||
innerClassOn: {
|
||||
type: String,
|
||||
default: null,
|
||||
required: true
|
||||
},
|
||||
innerClassOff: {
|
||||
type: String,
|
||||
default: null,
|
||||
required: true
|
||||
},
|
||||
id: {
|
||||
type: String,
|
||||
default: function () {
|
||||
return 'checkbox-id-' + this._uid;
|
||||
},
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
value: {
|
||||
default: null,
|
||||
},
|
||||
modelValue: {
|
||||
default: undefined,
|
||||
},
|
||||
checked: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
required: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
model: {}
|
||||
},
|
||||
|
||||
computed: {
|
||||
state() {
|
||||
if (this.modelValue === undefined) {
|
||||
return this.checked;
|
||||
}
|
||||
|
||||
if (Array.isArray(this.modelValue)) {
|
||||
return this.modelValue.indexOf(this.value) > -1;
|
||||
}
|
||||
return !!this.modelValue;
|
||||
},
|
||||
stateClass() {
|
||||
return this.onClass;
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
onChange() {
|
||||
this.toggle();
|
||||
},
|
||||
|
||||
toggle() {
|
||||
let value;
|
||||
|
||||
if (Array.isArray(this.modelValue)) {
|
||||
value = this.modelValue.slice(0);
|
||||
|
||||
if (this.state) {
|
||||
value.splice(value.indexOf(this.value), 1);
|
||||
} else {
|
||||
value.push(this.value);
|
||||
}
|
||||
} else {
|
||||
value = !this.state;
|
||||
}
|
||||
|
||||
this.$emit('input', value);
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
checked(newValue) {
|
||||
if (newValue !== this.state) {
|
||||
this.toggle();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
if (this.checked && !this.state) {
|
||||
this.toggle();
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
@ -18,8 +18,6 @@
|
||||
@import "~styles/sass-base";
|
||||
|
||||
.c-inspector {
|
||||
min-width: 150px;
|
||||
|
||||
> [class*="__"] {
|
||||
min-height: 50px;
|
||||
|
||||
@ -52,7 +50,7 @@
|
||||
}
|
||||
|
||||
/************************************************************** LEGACY */
|
||||
// TODO: refactor when markup can be converted
|
||||
// TODO: refactor when legacy properties markup can be converted
|
||||
.inspector-location {
|
||||
display: inline-block;
|
||||
|
||||
@ -93,10 +91,45 @@
|
||||
width: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.l-inspector-part {
|
||||
display: contents;
|
||||
}
|
||||
|
||||
h2 {
|
||||
@include propertiesHeader();
|
||||
font-size: 0.65rem;
|
||||
grid-column: 1 / 3;
|
||||
}
|
||||
|
||||
.tree .grid-properties {
|
||||
margin-left: $treeItemIndent + $interiorMarginLg;
|
||||
}
|
||||
}
|
||||
|
||||
.c-properties {
|
||||
@include gridTwoColumn();
|
||||
display: grid;
|
||||
grid-row-gap: 0;
|
||||
grid-template-columns: 1fr 2fr;
|
||||
align-items: start;
|
||||
min-width: 150px;
|
||||
|
||||
[class*="header"] {
|
||||
@include propertiesHeader();
|
||||
|
||||
&:not(:first-child) {
|
||||
// Allow multiple headers within a component
|
||||
margin-top: $interiorMarginLg;
|
||||
}
|
||||
}
|
||||
|
||||
[class*="span-all"],
|
||||
[class*="header"] {
|
||||
grid-column: 1 / 3;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
+ .c-properties {
|
||||
// Margin between components
|
||||
@ -139,7 +172,7 @@
|
||||
word-break: break-all;
|
||||
&:first-child {
|
||||
// If there is no preceding .label element, make value span columns
|
||||
@include gridTwoColumnSpanCols();
|
||||
grid-column: 1 / 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
<template>
|
||||
<div class="c-properties"></div>
|
||||
<div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
@ -10,22 +11,26 @@
|
||||
inject: ['openmct'],
|
||||
mounted() {
|
||||
this.openmct.selection.on('change', this.updateSelection);
|
||||
this.updateSelection(this.openmct.selection.get());
|
||||
this.updateSelection();
|
||||
},
|
||||
destroyed() {
|
||||
this.openmct.selection.off('change', this.updateSelection);
|
||||
},
|
||||
methods: {
|
||||
updateSelection(selection) {
|
||||
updateSelection() {
|
||||
let selection = this.openmct.selection.get();
|
||||
if (this.selectedView && this.selectedView.destroy) {
|
||||
this.selectedView.destroy();
|
||||
}
|
||||
delete this.viewContainer;
|
||||
this.$el.innerHTML = '';
|
||||
}
|
||||
this.selectedView = this.openmct.inspectorViews.get(selection);
|
||||
if (!this.selectedView) {
|
||||
return;
|
||||
}
|
||||
this.selectedView.show(this.$el);
|
||||
this.viewContainer = document.createElement('div');
|
||||
this.$el.append(this.viewContainer)
|
||||
this.selectedView.show(this.viewContainer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -39,8 +39,10 @@
|
||||
</div>
|
||||
<!-- Action buttons -->
|
||||
<div class="l-browse-bar__actions">
|
||||
<button class="l-browse-bar__actions__notebook-entry c-button icon-notebook" title="New Notebook entry"></button>
|
||||
<button class="l-browse-bar__actions__edit c-button c-button--major icon-pencil" title="Edit"></button>
|
||||
<button class="l-browse-bar__actions__edit c-button icon-notebook" title="New Notebook entry"></button>
|
||||
<button class="l-browse-bar__actions__notebook-entry c-button c-button--major icon-pencil" title="Edit" v-if="!isEditing" @click="edit()"></button>
|
||||
<button class="l-browse-bar__actions c-button c-button--major icon-save" title="Save and Finish Editing" v-if="isEditing" @click="saveAndFinishEditing()"></button>
|
||||
<button class="l-browse-bar__actions c-button icon-x" title="Cancel Editing" v-if="isEditing" @click="cancelEditing()"></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -65,13 +67,28 @@
|
||||
this.openmct.router.updateParams({
|
||||
view: this.viewKey
|
||||
});
|
||||
},
|
||||
edit() {
|
||||
this.openmct.editor.edit();
|
||||
},
|
||||
cancelEditing() {
|
||||
this.openmct.editor.cancel();
|
||||
},
|
||||
saveAndFinishEditing() {
|
||||
this.openmct.editor.save().then(()=> {
|
||||
this.openmct.notifications.info('Save successful');
|
||||
}).catch((error) => {
|
||||
this.openmct.notifications.error('Error saving objects');
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
showViewMenu: false,
|
||||
domainObject: {},
|
||||
viewKey: undefined
|
||||
viewKey: undefined,
|
||||
isEditing: this.openmct.editor.isEditing()
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -105,6 +122,10 @@
|
||||
this.showViewMenu = false;
|
||||
}
|
||||
});
|
||||
|
||||
this.openmct.editor.on('isEditing', (isEditing) => {
|
||||
this.isEditing = isEditing;
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@ -126,7 +147,6 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1 1 auto;
|
||||
font-size: 1.4em;
|
||||
margin-right: $interiorMargin;
|
||||
min-width: 0; // Forces interior to compress when pushed on
|
||||
}
|
||||
@ -158,6 +178,7 @@
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex: 0 1 auto;
|
||||
font-size: 1.4em;
|
||||
min-width: 0;
|
||||
|
||||
&:before {
|
||||
|
@ -1,5 +1,7 @@
|
||||
<template>
|
||||
<div class="l-shell">
|
||||
<div class="l-shell" :class="{
|
||||
'is-editing': isEditing
|
||||
}">
|
||||
<div class="l-shell__head">
|
||||
<CreateButton class="l-shell__create-button"></CreateButton>
|
||||
<div class="l-shell__controls">
|
||||
@ -23,14 +25,13 @@
|
||||
<div class="l-shell__search">
|
||||
<search class="c-search--major" ref="shell-search"></search>
|
||||
</div>
|
||||
<div class="l-shell__tree">
|
||||
<mct-tree></mct-tree>
|
||||
</div>
|
||||
<mct-tree class="l-shell__tree"></mct-tree>
|
||||
</pane>
|
||||
<pane class="l-shell__pane-main">
|
||||
<browse-bar class="l-shell__main-view-browse-bar"
|
||||
ref="browseBar">
|
||||
</browse-bar>
|
||||
<toolbar class="l-shell__toolbar"></toolbar>
|
||||
<object-view class="l-shell__main-container"
|
||||
ref="browseObject">
|
||||
</object-view>
|
||||
@ -46,7 +47,7 @@
|
||||
</pane>
|
||||
</multipane>
|
||||
<div class="l-shell__status">
|
||||
<MctStatus></MctStatus>
|
||||
<StatusBar></StatusBar>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -60,47 +61,67 @@
|
||||
top: 0; right: 0; bottom: 0; left: 0;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
overflow: hidden;
|
||||
|
||||
&__status {
|
||||
background: $colorBodyFg;
|
||||
color: $colorBodyBg;
|
||||
border-top: 1px solid $colorInteriorBorder;
|
||||
background: $colorStatusBarBg;
|
||||
color: $colorStatusBarFg;
|
||||
height: 24px;
|
||||
padding: $interiorMarginSm;
|
||||
}
|
||||
|
||||
&__pane-tree {
|
||||
background: $colorTreeBg;
|
||||
width: 40%;
|
||||
|
||||
[class*="collapse-button"] {
|
||||
// For mobile, collapse button becomes menu icon
|
||||
body.mobile & {
|
||||
height: $mobileMenuIconD;
|
||||
width: $mobileMenuIconD;
|
||||
@include cClickIcon();
|
||||
position: absolute;
|
||||
right: -2 * nth($shellPanePad, 2); // Needs to be -1 * when pane is collapsed
|
||||
top: 0;
|
||||
transform: translateX(100%);
|
||||
width: $mobileMenuIconD;
|
||||
z-index: 2;
|
||||
|
||||
&:before {
|
||||
color: $colorKey;
|
||||
content: $glyph-icon-menu-hamburger;
|
||||
font-family: symbolsfont;
|
||||
font-size: 1.4em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__pane-main,
|
||||
&__pane-tree {
|
||||
> .l-pane__contents {
|
||||
&__pane-tree,
|
||||
&__pane-inspector,
|
||||
&__pane-main {
|
||||
.l-pane__contents {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
|
||||
> * {
|
||||
flex: 0 0 auto;
|
||||
+ * {
|
||||
margin-top: $interiorMarginLg;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
body.mobile & {
|
||||
&__pane-tree {
|
||||
background: linear-gradient(90deg, transparent 70%, rgba(black, 0.2) 99%, rgba(black, 0.3));
|
||||
|
||||
&[class*="--collapsed"] {
|
||||
[class*="collapse-button"] {
|
||||
right: -1 * nth($shellPanePad, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
body.phone.portrait & {
|
||||
&__pane-tree {
|
||||
width: calc(100% - #{$mobileMenuIconD});
|
||||
width: calc(100% - #{$mobileMenuIconD + (2 * nth($shellPanePad, 2))});
|
||||
|
||||
+ .l-pane {
|
||||
// Hide pane-main when this pane is expanded
|
||||
@ -136,13 +157,13 @@
|
||||
}
|
||||
|
||||
body.mobile & .l-shell__main-view-browse-bar {
|
||||
margin-left: $mobileMenuIconD - $interiorMarginLg; // Make room for the hamburger!
|
||||
margin-left: $mobileMenuIconD; // Make room for the hamburger!
|
||||
}
|
||||
|
||||
&__head {
|
||||
align-items: center;
|
||||
background: $colorHeadBg;
|
||||
justify-content: space-between;
|
||||
border-bottom: 1px solid $colorInteriorBorder;
|
||||
padding: $interiorMargin;
|
||||
|
||||
> [class*="__"] + [class*="__"] {
|
||||
@ -162,28 +183,41 @@
|
||||
margin-right: 2.5%;
|
||||
}
|
||||
|
||||
/********** MAIN AREA */
|
||||
/******************************* MAIN AREA */
|
||||
|
||||
&__main-container {
|
||||
// Wrapper for main views
|
||||
flex: 1 1 auto;
|
||||
flex: 1 1 auto !important;
|
||||
overflow: auto;
|
||||
//font-size: 16px; // TEMP FOR LEGACY STYLING
|
||||
}
|
||||
|
||||
&__tree {
|
||||
// Tree component within __pane-tree
|
||||
flex: 1 1 100%;
|
||||
overflow-y: auto;
|
||||
flex: 1 1 auto !important;
|
||||
}
|
||||
|
||||
&__time-conductor {
|
||||
border-top: 1px solid $colorInteriorBorder;
|
||||
flex: 0 0 auto;
|
||||
padding-top: $interiorMargin;
|
||||
}
|
||||
|
||||
&__main {
|
||||
> .l-pane {
|
||||
padding: nth($shellPanePad, 1) nth($shellPanePad, 2);
|
||||
}
|
||||
}
|
||||
|
||||
body.desktop & {
|
||||
/********** HEAD AND STATUS */
|
||||
&__main {
|
||||
// Top and bottom padding in container that holds tree, __pane-main and Inspector
|
||||
padding: $shellMainPad;
|
||||
> .l-pane {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__pane-tree,
|
||||
&__pane-inspector {
|
||||
max-width: 30%;
|
||||
@ -202,7 +236,6 @@
|
||||
|
||||
<script>
|
||||
import Inspector from '../inspector/Inspector.vue';
|
||||
import MctStatus from './MctStatus.vue';
|
||||
import MctTree from './mct-tree.vue';
|
||||
import ObjectView from './ObjectView.vue';
|
||||
import MctTemplate from '../legacy/mct-template.vue';
|
||||
@ -212,6 +245,8 @@
|
||||
import multipane from '../controls/multipane.vue';
|
||||
import pane from '../controls/pane.vue';
|
||||
import BrowseBar from './BrowseBar.vue';
|
||||
import StatusBar from './status-bar/StatusBar.vue';
|
||||
import Toolbar from './Toolbar.vue';
|
||||
|
||||
var enterFullScreen = () => {
|
||||
var docElm = document.documentElement;
|
||||
@ -242,9 +277,9 @@
|
||||
}
|
||||
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
components: {
|
||||
Inspector,
|
||||
MctStatus,
|
||||
MctTree,
|
||||
ObjectView,
|
||||
'mct-template': MctTemplate,
|
||||
@ -253,12 +288,20 @@
|
||||
search,
|
||||
multipane,
|
||||
pane,
|
||||
BrowseBar
|
||||
BrowseBar,
|
||||
StatusBar,
|
||||
Toolbar
|
||||
},
|
||||
mounted() {
|
||||
this.openmct.editor.on('isEditing', (isEditing)=>{
|
||||
this.isEditing = isEditing;
|
||||
});
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
fullScreen: false,
|
||||
conductorComponent: {}
|
||||
conductorComponent: {},
|
||||
isEditing: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
@ -1,14 +0,0 @@
|
||||
<template>
|
||||
<span class="c-status">
|
||||
[ Status ]
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
}
|
||||
</script>
|
@ -2,7 +2,7 @@
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.l-object-view {
|
||||
.c-object-view {
|
||||
display: contents;
|
||||
}
|
||||
</style>
|
||||
@ -33,6 +33,7 @@ export default {
|
||||
this.debounceUpdateView = _.debounce(this.updateView, 10);
|
||||
},
|
||||
mounted() {
|
||||
this.currentObject = this.object;
|
||||
this.updateView();
|
||||
},
|
||||
methods: {
|
||||
@ -50,7 +51,7 @@ export default {
|
||||
return;
|
||||
}
|
||||
this.viewContainer = document.createElement('div');
|
||||
this.viewContainer.classList.add('l-object-view');
|
||||
this.viewContainer.classList.add('c-object-view');
|
||||
this.$el.append(this.viewContainer);
|
||||
let provider = this.openmct.objectViews.getByProviderKey(this.viewKey);
|
||||
if (!provider) {
|
||||
|
291
src/ui/components/layout/Toolbar.vue
Normal file
291
src/ui/components/layout/Toolbar.vue
Normal file
@ -0,0 +1,291 @@
|
||||
<template>
|
||||
<div class="c-toolbar">
|
||||
<!-- VERSION MANUALLY RESTORED FROM VUE-LAYOUT -->
|
||||
<div class="c-button-set">
|
||||
<div class="c-ctrl-wrapper">
|
||||
<div class="c-button--menu js-add-button icon-plus"
|
||||
@click="toggleMenus">
|
||||
<div class="c-button__label">Add</div>
|
||||
</div>
|
||||
<div class="c-menu" v-if="showMenus">
|
||||
<ul>
|
||||
<li v-for="item in addMenuItems"
|
||||
:class="item.class"
|
||||
:title="item.title">
|
||||
{{ item.name }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="c-button-set"
|
||||
v-if="toolsItemSelected">
|
||||
<div class="c-ctrl-wrapper"
|
||||
v-if="toolsItemSelected">
|
||||
<div class="c-click-icon c-click-icon--menu js-layers icon-layers"
|
||||
@click="toggleMenus"></div>
|
||||
<div class="c-menu" v-if="showMenus">
|
||||
<ul>
|
||||
<li v-for="item in layersMenuItems"
|
||||
:class="item.class"
|
||||
:title="item.title">
|
||||
{{ item.name }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="c-ctrl-wrapper"
|
||||
v-if="toolsColorFill">
|
||||
<div class="c-click-icon c-click-icon--swatched js-color-fill icon-paint-bucket"
|
||||
@click="toggleMenus">
|
||||
<div class="c-swatch" style="background: #33ff00;"></div>
|
||||
</div>
|
||||
<div class="c-menu c-palette c-palette--color"
|
||||
v-if="showMenus">
|
||||
<div class="c-palette__item-none"
|
||||
vif="this.palette.itemNone === true">
|
||||
<div class="c-palette__item"
|
||||
@click="this.setColor('no-color')"></div>
|
||||
No fill
|
||||
</div>
|
||||
<div class="c-palette__items">
|
||||
<div class="c-palette__item"
|
||||
v-for="color in colorPalette"
|
||||
:style="{ background: color.value }"
|
||||
@click="this.setColor(color.value)"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="c-ctrl-wrapper"
|
||||
v-if="toolsColorStroke">
|
||||
<div class="c-click-icon c-click-icon--swatched js-color-stroke icon-pencil">
|
||||
<div class="c-toolbar-button__swatch" style="background: #ffffff;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="c-ctrl-wrapper"
|
||||
v-if="toolsColorText">
|
||||
<div class="c-click-icon c-click-icon--swatched js-color-text icon-font">
|
||||
<div class="c-toolbar-button__swatch" style="background: #333333;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="c-button-set"
|
||||
v-if="toolsItemSelected && toolsFontSize">
|
||||
<div class="c-ctrl-wrapper">
|
||||
<div class="c-click-icon c-click-icon--menu js-font-size"
|
||||
@click="toggleMenus">
|
||||
<div class="c-button__label">11 px</div>
|
||||
</div>
|
||||
<div class="c-menu" v-if="showMenus">
|
||||
<ul>
|
||||
<li v-for="item in fontSizeMenuItems">
|
||||
{{ item.name }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="c-button-set"
|
||||
v-if="toolsItemSelected && toolsEditProperties">
|
||||
<div class="c-ctrl-wrapper">
|
||||
<div class="c-click-icon js-image icon-gear"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="c-button-set"
|
||||
v-if="toolsItemSelected">
|
||||
<labeledNumberInput label="X" value=1 title="X position"></labeledNumberInput>
|
||||
<labeledNumberInput label="Y" value=2 title="Y position"></labeledNumberInput>
|
||||
<labeledNumberInput label="W" value=3 title="Width"></labeledNumberInput>
|
||||
<labeledNumberInput label="H" value=4 title="Height"></labeledNumberInput>
|
||||
</div>
|
||||
|
||||
<div class="c-button-set"
|
||||
v-if="toolsItemSelected">
|
||||
<div class="c-click-icon c-click-icon--caution icon-trash"></div>
|
||||
</div>
|
||||
|
||||
<div class="c-button-set"
|
||||
v-if="toolsItemSelected">
|
||||
<checkbox checked title="This is a checkbox">Checkbox</checkbox>
|
||||
</div>
|
||||
|
||||
<div class="c-button-set"
|
||||
v-if="toolsItemSelected">
|
||||
<toggle-button title="Toggle frame" checked
|
||||
class="c-click-icon"
|
||||
inner-class-on="icon-frame-show"
|
||||
inner-class-off="icon-frame-hide"></toggle-button>
|
||||
<toggle-button title="Snap to grid" checked
|
||||
class="c-click-icon"
|
||||
inner-class-on="icon-grid-snap-to"
|
||||
inner-class-off="icon-grid-snap-no"></toggle-button>
|
||||
<toggle-button title="Show label and value" checked
|
||||
class="c-click-icon"
|
||||
inner-class-on="icon-two-parts-both"
|
||||
inner-class-off="icon-two-parts-one-only"></toggle-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import labeledNumberInput from '../controls/labeledNumberInput.vue';
|
||||
import checkbox from '../controls/checkboxCustom.vue';
|
||||
import toggleButton from '../controls/toggleButton.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
labeledNumberInput,
|
||||
checkbox,
|
||||
toggleButton
|
||||
},
|
||||
methods: {
|
||||
toggleMenus: function () {
|
||||
this.showMenus = !this.showMenus;
|
||||
}
|
||||
},
|
||||
props: {
|
||||
toolsItemSelected: { type: Boolean, default: true },
|
||||
toolsColorFill: { type: Boolean, default: true },
|
||||
toolsColorStroke: { type: Boolean, default: true },
|
||||
toolsColorText: { type: Boolean, default: true },
|
||||
toolsFontSize: { type: Boolean, default: true },
|
||||
toolsEditProperties: { type: Boolean, default: true },
|
||||
toolSetBox: ['toolsColorFill', 'toolsColorStroke'],
|
||||
toolSetLine: ['toolsColorStroke'],
|
||||
toolSetText: ['toolsColorFill', 'toolsColorStroke', 'toolsColorText', 'toolsFontSize', 'toolsEditProperties'],
|
||||
toolSetImage: ['toolsColorStroke', 'toolsEditProperties'],
|
||||
toolSetTelemetry: ['toolsColorFill', 'toolsColorStroke', 'toolsColorText', 'toolsFontSize', 'toolsLabelValue']
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
showMenus: false,
|
||||
addMenuItems: [
|
||||
{ name: 'Box', class: 'icon-box', title: 'Add Box' },
|
||||
{ name: 'Line', class: 'icon-line-horz', title: 'Add Line' },
|
||||
{ name: 'Text', class: 'icon-font', title: 'Add Text' },
|
||||
{ name: 'Image', class: 'icon-image', title: 'Add Image' }
|
||||
],
|
||||
layersMenuItems: [
|
||||
{ name: 'Move to top', class: 'icon-arrow-double-up', title: 'Move to top' },
|
||||
{ name: 'Move up', class: 'icon-arrow-up', title: 'Move up' },
|
||||
{ name: 'Move down', class: 'icon-arrow-down', title: 'Move down' },
|
||||
{ name: 'Move to bottom', class: 'icon-arrow-double-down', title: 'Move to bottom' }
|
||||
],
|
||||
fontSizeMenuItems: [
|
||||
{ value: '9', name: '9 px' },
|
||||
{ value: '10', name: '10 px' },
|
||||
{ value: '11', name: '11 px' },
|
||||
{ value: '12', name: '12 px' },
|
||||
{ value: '13', name: '13 px' },
|
||||
{ value: '14', name: '14 px' },
|
||||
{ value: '16', name: '16 px' },
|
||||
{ value: '18', name: '18 px' },
|
||||
{ value: '20', name: '20 px' },
|
||||
{ value: '24', name: '24 px' },
|
||||
{ value: '28', name: '28 px' },
|
||||
{ value: '32', name: '32 px' },
|
||||
{ value: '40', name: '40 px' },
|
||||
{ value: '48', name: '48 px' },
|
||||
{ value: '56', name: '56 px' },
|
||||
{ value: '64', name: '64 px' },
|
||||
{ value: '72', name: '72 px' },
|
||||
{ value: '80', name: '80 px' },
|
||||
{ value: '88', name: '88 px' },
|
||||
{ value: '96', name: '96 px' },
|
||||
{ value: '128', name: '128 px' },
|
||||
{ value: '160', name: '160 px' }
|
||||
],
|
||||
colorPalette: [
|
||||
{ value: '#000000' },
|
||||
{ value: '#434343' },
|
||||
{ value: '#666666' },
|
||||
{ value: '#999999' },
|
||||
{ value: '#b7b7b7' },
|
||||
{ value: '#cccccc' },
|
||||
{ value: '#d9d9d9' },
|
||||
{ value: '#efefef' },
|
||||
{ value: '#f3f3f3' },
|
||||
{ value: '#ffffff' },
|
||||
{ value: '#980000' },
|
||||
{ value: '#ff0000' },
|
||||
{ value: '#ff9900' },
|
||||
{ value: '#ffff00' },
|
||||
{ value: '#00ff00' },
|
||||
{ value: '#00ffff' },
|
||||
{ value: '#4a86e8' },
|
||||
{ value: '#0000ff' },
|
||||
{ value: '#9900ff' },
|
||||
{ value: '#ff00ff' },
|
||||
{ value: '#e6b8af' },
|
||||
{ value: '#f4cccc' },
|
||||
{ value: '#fce5cd' },
|
||||
{ value: '#fff2cc' },
|
||||
{ value: '#d9ead3' },
|
||||
{ value: '#d0e0e3' },
|
||||
{ value: '#c9daf8' },
|
||||
{ value: '#cfe2f3' },
|
||||
{ value: '#d9d2e9' },
|
||||
{ value: '#ead1dc' },
|
||||
{ value: '#dd7e6b' },
|
||||
{ value: '#dd7e6b' },
|
||||
{ value: '#f9cb9c' },
|
||||
{ value: '#ffe599' },
|
||||
{ value: '#b6d7a8' },
|
||||
{ value: '#a2c4c9' },
|
||||
{ value: '#a4c2f4' },
|
||||
{ value: '#9fc5e8' },
|
||||
{ value: '#b4a7d6' },
|
||||
{ value: '#d5a6bd' },
|
||||
{ value: '#cc4125' },
|
||||
{ value: '#e06666' },
|
||||
{ value: '#f6b26b' },
|
||||
{ value: '#ffd966' },
|
||||
{ value: '#93c47d' },
|
||||
{ value: '#76a5af' },
|
||||
{ value: '#6d9eeb' },
|
||||
{ value: '#6fa8dc' },
|
||||
{ value: '#8e7cc3' },
|
||||
{ value: '#c27ba0' },
|
||||
{ value: '#a61c00' },
|
||||
{ value: '#cc0000' },
|
||||
{ value: '#e69138' },
|
||||
{ value: '#f1c232' },
|
||||
{ value: '#6aa84f' },
|
||||
{ value: '#45818e' },
|
||||
{ value: '#3c78d8' },
|
||||
{ value: '#3d85c6' },
|
||||
{ value: '#674ea7' },
|
||||
{ value: '#a64d79' },
|
||||
{ value: '#85200c' },
|
||||
{ value: '#990000' },
|
||||
{ value: '#b45f06' },
|
||||
{ value: '#bf9000' },
|
||||
{ value: '#38761d' },
|
||||
{ value: '#134f5c' },
|
||||
{ value: '#1155cc' },
|
||||
{ value: '#0b5394' },
|
||||
{ value: '#351c75' },
|
||||
{ value: '#741b47' },
|
||||
{ value: '#5b0f00' },
|
||||
{ value: '#660000' },
|
||||
{ value: '#783f04' },
|
||||
{ value: '#7f6000' },
|
||||
{ value: '#274e13' },
|
||||
{ value: '#0c343d' },
|
||||
{ value: '#1c4587' },
|
||||
{ value: '#073763' },
|
||||
{ value: '#20124d' },
|
||||
{ value: '#4c1130' }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
@ -1,10 +1,12 @@
|
||||
<template>
|
||||
<div class="c-tree__wrapper">
|
||||
<ul class="c-tree">
|
||||
<tree-item v-for="child in children"
|
||||
:key="child.id"
|
||||
:node="child">
|
||||
</tree-item>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@ -16,6 +18,11 @@
|
||||
overflow-y: auto;
|
||||
height: 100%;
|
||||
|
||||
&__wrapper {
|
||||
overflow-y: auto;
|
||||
padding-right: $interiorMarginSm;
|
||||
}
|
||||
|
||||
.c-tree {
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
41
src/ui/components/layout/status-bar/Indicators.vue
Normal file
41
src/ui/components/layout/status-bar/Indicators.vue
Normal file
@ -0,0 +1,41 @@
|
||||
<!--
|
||||
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>
|
||||
<span id='status' class='status-holder'></span>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
|
||||
mounted() {
|
||||
this.openmct.indicators.indicatorObjects.forEach((indicator) => {
|
||||
// So that we can consistently position indicator elements,
|
||||
// guarantee that they are wrapped in an element we control
|
||||
var wrapperNode = document.createElement('span');
|
||||
wrapperNode.className = 'l-indicator';
|
||||
wrapperNode.appendChild(indicator.element);
|
||||
this.$el.appendChild(wrapperNode);
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
98
src/ui/components/layout/status-bar/NotificationBanner.vue
Normal file
98
src/ui/components/layout/status-bar/NotificationBanner.vue
Normal file
@ -0,0 +1,98 @@
|
||||
<!--
|
||||
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-message-banner s-message-banner"
|
||||
:class="[
|
||||
activeModel.severity,
|
||||
{
|
||||
'minimized': activeModel.minimized,
|
||||
'new': !activeModel.minimized
|
||||
}]"
|
||||
v-if="activeModel">
|
||||
<span @click="maximize()" class="banner-elem label">{{activeModel.title}}</span>
|
||||
<span @click="maximize()" v-if="activeModel.progress !== undefined || activeModel.unknownProgress">
|
||||
<div class="banner-elem"><!-- was mct-include -->
|
||||
<span class="l-progress-bar s-progress-bar"
|
||||
:class="{'indeterminate': activeModel.unknownProgress }">
|
||||
<span class="progress-amt-holder">
|
||||
<span class="progress-amt" :style="progressWidth"></span>
|
||||
</span>
|
||||
</span>
|
||||
<div class="progress-info hint" v-if="activeModel.progressText !== undefined">
|
||||
<span class="progress-amt-text" v-if="activeModel.progress > 0">{{activeModel.progress}}% complete. </span>
|
||||
{{activeModel.progressText}}
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
<a class="close icon-x" @click="dismiss()"></a>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.l-message-banner {
|
||||
display: inline;
|
||||
left: 50%;
|
||||
position: absolute;
|
||||
}
|
||||
.banner-elem {
|
||||
display: inline;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
let activeNotification = undefined;
|
||||
let dialogService = undefined;
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
data() {
|
||||
return {
|
||||
activeModel: undefined
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
showNotification(notification) {
|
||||
activeNotification = notification;
|
||||
this.activeModel = notification.model;
|
||||
activeNotification.once('destroy', () => {
|
||||
if (this.activeModel === notification.model){
|
||||
this.activeModel = undefined;
|
||||
activeNotification = undefined;
|
||||
}
|
||||
});
|
||||
},
|
||||
dismiss() {
|
||||
activeNotification.dismissOrMinimize();
|
||||
},
|
||||
maximize() {
|
||||
//Not implemented yet.
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
progressWidth() {
|
||||
return {
|
||||
width: this.activeModel.progress + '%'
|
||||
};
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
openmct.notifications.on('notification', this.showNotification);
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
42
src/ui/components/layout/status-bar/StatusBar.vue
Normal file
42
src/ui/components/layout/status-bar/StatusBar.vue
Normal file
@ -0,0 +1,42 @@
|
||||
<!--
|
||||
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>
|
||||
<span class="c-status">
|
||||
<indicators></indicators>
|
||||
<notification-banner></notification-banner>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.c-status {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import Indicators from './Indicators.vue';
|
||||
import NotificationBanner from './NotificationBanner.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Indicators,
|
||||
NotificationBanner
|
||||
}
|
||||
}
|
||||
</script>
|
@ -8,6 +8,8 @@
|
||||
@click="toggleChildren">
|
||||
</view-control>
|
||||
<a class="c-tree__item__label"
|
||||
draggable="true"
|
||||
@dragstart="dragStart"
|
||||
:href="href">
|
||||
<div class="c-tree__item__type-icon"
|
||||
:class="cssClass"></div>
|
||||
@ -28,7 +30,7 @@
|
||||
import viewControl from '../controls/viewControl.vue'
|
||||
export default {
|
||||
name: 'tree-item',
|
||||
inject: ['openmct'],
|
||||
inject: ['openmct', 'domainObject'],
|
||||
props: {
|
||||
node: Object
|
||||
},
|
||||
@ -89,6 +91,9 @@
|
||||
.then(() => this.loaded = true);
|
||||
}
|
||||
},
|
||||
dragStart($event) {
|
||||
$event.dataTransfer.setData("domainObject", JSON.stringify(this.node.object));
|
||||
}
|
||||
},
|
||||
components: {
|
||||
viewControl
|
||||
|
@ -11,6 +11,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ContextMenuDirective from './ContextMenuDirective.js';
|
||||
|
||||
export default {
|
||||
data: function () {
|
||||
return {
|
10
src/ui/context-menu/ContextMenuDirective.js
Normal file
10
src/ui/context-menu/ContextMenuDirective.js
Normal file
@ -0,0 +1,10 @@
|
||||
import ContextMenuGesture from './ContextMenuGesture.js';
|
||||
|
||||
export default {
|
||||
bind(element, binding) {
|
||||
binding.vnode.context.destroy = new ContextMenuGesture(element, binding.value);
|
||||
},
|
||||
unbind(){
|
||||
|
||||
}
|
||||
}
|
31
src/ui/context-menu/ContextMenuGesture.js
Normal file
31
src/ui/context-menu/ContextMenuGesture.js
Normal file
@ -0,0 +1,31 @@
|
||||
import ContextMenu from '../components/ContextMenu.vue';
|
||||
import Vue from 'vue';
|
||||
|
||||
export default function ContextMenuGesture (element, object) {
|
||||
let vm;
|
||||
element.addEventListener('context', showContextMenu);
|
||||
|
||||
function showContextMenu(event){
|
||||
vm = new Vue({
|
||||
...ContextMenu
|
||||
});
|
||||
document.body.appendChild(vm.$el);
|
||||
document.addEventListener('click', hideContextMenu, {
|
||||
capture: true,
|
||||
once: true
|
||||
});
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
function hideContextMenu() {
|
||||
vm.destroy();
|
||||
document.body.removeChild(vm.$el);
|
||||
}
|
||||
|
||||
return function destroy() {
|
||||
element.removeEventListener('context', this.showContextMenu);
|
||||
document.removeEventListener('click', hideContextMenu);
|
||||
}
|
||||
}
|
||||
}
|
91
src/ui/overlayService/overlay.vue
Normal file
91
src/ui/overlayService/overlay.vue
Normal file
@ -0,0 +1,91 @@
|
||||
<template>
|
||||
<div class="c-overlay">
|
||||
<div class="c-overlay__blocker"
|
||||
v-on:click="destroy">
|
||||
</div>
|
||||
<div class="c-overlay__outer">
|
||||
<button class="c-click-icon c-overlay__close-button icon-x-in-circle"
|
||||
v-on:click="destroy">
|
||||
</button>
|
||||
<div class="c-overlay__contents" ref="element"></div>
|
||||
<div class="c-overlay__button-bar">
|
||||
<button class="c-button c-button--major"
|
||||
v-on:click="destroy">Done</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@import "~styles/sass-base";
|
||||
|
||||
.l-overlay-wrapper {
|
||||
// Created by overlayService.js, contains this template.
|
||||
// Acts as an anchor for one or more overlays.
|
||||
display: contents;
|
||||
}
|
||||
|
||||
.c-overlay {
|
||||
@include abs();
|
||||
z-index: 100;
|
||||
|
||||
&__blocker {
|
||||
display: none; // Mobile-first
|
||||
}
|
||||
|
||||
&__outer {
|
||||
@include abs();
|
||||
background: $overlayColorBg;
|
||||
color: $overlayColorFg;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: $overlayInnerMargin;
|
||||
}
|
||||
|
||||
&__close-button {
|
||||
$p: $interiorMarginSm;
|
||||
border-radius: 100%;
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
top: $p; right: $p;
|
||||
}
|
||||
|
||||
&__contents {
|
||||
flex: 1 1 auto;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
&__button-bar {
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: $interiorMargin;
|
||||
}
|
||||
|
||||
body.desktop & {
|
||||
&__blocker {
|
||||
@include abs();
|
||||
background: rgba(black, 0.7);
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
}
|
||||
|
||||
&__outer {
|
||||
$m: $overlayOuterMargin;
|
||||
top: $m; right: $m; bottom: $m; left: $m;
|
||||
border-radius: $overlayCr;
|
||||
box-shadow: rgba(black, 0.5) 0 2px 25px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
inject: ['destroy', 'element'],
|
||||
mounted() {
|
||||
this.$refs.element.appendChild(this.element);
|
||||
}
|
||||
}
|
||||
</script>
|
87
src/ui/overlayService/overlayService.js
Normal file
87
src/ui/overlayService/overlayService.js
Normal file
@ -0,0 +1,87 @@
|
||||
/*****************************************************************************
|
||||
* 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([
|
||||
'./overlay.vue',
|
||||
'vue'
|
||||
], function (
|
||||
OverlayComponent,
|
||||
Vue
|
||||
) {
|
||||
|
||||
function OverlayService() {
|
||||
this.activeOverlays = [];
|
||||
this.overlayId = 0;
|
||||
}
|
||||
|
||||
OverlayService.prototype.show = function (element, options) {
|
||||
if(this.activeOverlays.length) {
|
||||
this.activeOverlays[this.activeOverlays.length - 1].overlay.classList.add('invisible');
|
||||
}
|
||||
|
||||
let overlayTypeCssClass = options.cssClass, // Values could be l-large-view, l-dialog, l-message
|
||||
overlay = document.createElement('div'),
|
||||
component = new Vue({
|
||||
provide: {
|
||||
destroy: this.destroy.bind(this),
|
||||
element: element
|
||||
},
|
||||
components: {
|
||||
OverlayComponent: OverlayComponent.default
|
||||
},
|
||||
template: '<overlay-component></overlay-component>'
|
||||
});
|
||||
|
||||
overlay.classList.add('l-overlay-wrapper', overlayTypeCssClass);
|
||||
document.body.appendChild(overlay);
|
||||
|
||||
overlay.appendChild(component.$mount().$el);
|
||||
|
||||
this.activeOverlays.push({
|
||||
overlay: overlay,
|
||||
component: component,
|
||||
onDestroy: options.onDestroy,
|
||||
id: this.overlayId
|
||||
});
|
||||
|
||||
this.overlayId++;
|
||||
};
|
||||
|
||||
OverlayService.prototype.destroy = function () {
|
||||
var lastActiveOverlayObject = this.activeOverlays.pop(),
|
||||
lastActiveOverlay = lastActiveOverlayObject.overlay,
|
||||
lastActiveComponent = lastActiveOverlayObject.component;
|
||||
|
||||
if (lastActiveOverlayObject.onDestroy && typeof lastActiveOverlayObject.onDestroy === 'function') {
|
||||
lastActiveOverlayObject.onDestroy();
|
||||
}
|
||||
|
||||
lastActiveComponent.$destroy(true);
|
||||
document.body.removeChild(lastActiveOverlay);
|
||||
|
||||
if (this.activeOverlays.length) {
|
||||
this.activeOverlays[this.activeOverlays.length - 1].overlay.classList.remove('invisible');
|
||||
}
|
||||
};
|
||||
|
||||
return OverlayService;
|
||||
});
|
@ -7,12 +7,24 @@ define([
|
||||
return function install(openmct) {
|
||||
let navigateCall = 0;
|
||||
let browseObject;
|
||||
let removeSelectable = undefined;
|
||||
|
||||
|
||||
function viewObject(object, viewProvider) {
|
||||
if (removeSelectable) {
|
||||
removeSelectable();
|
||||
removeSelectable = undefined;
|
||||
}
|
||||
openmct.layout.$refs.browseObject.show(object, viewProvider.key);
|
||||
openmct.layout.$refs.browseBar.domainObject = object;
|
||||
openmct.layout.$refs.browseBar.viewKey = viewProvider.key;
|
||||
removeSelectable = openmct.selection.selectable(
|
||||
openmct.layout.$refs.browseObject.$el,
|
||||
{
|
||||
item: object
|
||||
},
|
||||
true
|
||||
);
|
||||
};
|
||||
|
||||
function navigateToPath(path, currentViewKey) {
|
||||
@ -22,16 +34,18 @@ define([
|
||||
if (!Array.isArray(path)) {
|
||||
path = path.split('/');
|
||||
}
|
||||
let keyString = path[path.length - 1];
|
||||
// TODO: retain complete path in navigation.
|
||||
return openmct.objects.get(keyString)
|
||||
.then((object) => {
|
||||
return Promise.all(path.map((keyString)=>{
|
||||
return openmct.objects.get(keyString);
|
||||
})).then((objects)=>{
|
||||
if (currentNavigation !== navigateCall) {
|
||||
return; // Prevent race.
|
||||
}
|
||||
openmct.layout.$refs.browseBar.domainObject = object;
|
||||
browseObject = object;
|
||||
if (!object) {
|
||||
|
||||
let navigatedObject = objects[objects.length - 1];
|
||||
|
||||
openmct.layout.$refs.browseBar.domainObject = navigatedObject;
|
||||
browseObject = navigatedObject;
|
||||
if (!navigatedObject) {
|
||||
openmct.layout.$refs.browseObject.clear();
|
||||
return;
|
||||
}
|
||||
@ -39,12 +53,12 @@ define([
|
||||
.objectViews
|
||||
.getByProviderKey(currentViewKey)
|
||||
|
||||
if (currentProvider && currentProvider.canView(object)) {
|
||||
viewObject(object, currentProvider);
|
||||
if (currentProvider && currentProvider.canView(navigatedObject)) {
|
||||
viewObject(navigatedObject, currentProvider);
|
||||
return;
|
||||
}
|
||||
|
||||
let defaultProvider = openmct.objectViews.get(object)[0];
|
||||
let defaultProvider = openmct.objectViews.get(navigatedObject)[0];
|
||||
if (defaultProvider) {
|
||||
openmct.router.updateParams({
|
||||
view: defaultProvider.key
|
||||
|
Reference in New Issue
Block a user