Compare commits

..

10 Commits

Author SHA1 Message Date
485e948abe Context Menu WIP 2018-10-11 10:50:09 -07:00
64b9d4c24a Vue status bar (#2188)
* Implemented indicators

* WIP

* Fixed templates from notifications example

* Message bar implemented

* Implemented notifications

* Fixed bug with destruction of notifications

* Renamed MessageBanner to NotificationBanner

* Add save success message

* Removed NotificationServiceSpec

* Removed legacy constants from bundle
2018-10-10 17:35:11 -07:00
88bcb6078e Conductor fixes (#2189)
* Conductor fixes

- Restore RT update time field;
- Colors tweaked;
- Much better mobile layout;

* Significant fixes in Conductor markup and styling

- Markup/CSS simplified and clearer;
- Better coloring in both Themes;
- Better clarity for axis UI element;
- Fixed hover and focus styles on inputs;
2018-10-10 17:03:52 -07:00
5f9f3cd8e8 Topic themes (#2187)
* Bringing over in-progress changes from topic-core-css

- Adds _espresso-constants.scss;
- Cleanup colors and naming;
- Remove conflict res leftover 'domainObject' in mct-tree.vue;
- Still WIP!

* Various

- Remove pushBack / pullForward functions;
- Fix c-input-inline, remove bg until hover;
- TODO: input bg colors
- Increased margin in main-pane;

* Themeing WIP

- Conductor markup: convert to buttons for accessibility;
- Conductor styles consolidated and changed for better theme support;

* Themeing WIP; significant rewrite of pane headers

- Pane headers restructured for better semantics and clarity;
- Espresso design refined and tightened;
- Grid Vue changes for better themeing support;
- TODO: fix mobile version, collapse icon is whack;

* Restored Number-type input styling for correct positioning of spinner
button;

* Themeing mods for click-icon styles

* Bring Snow theme into style parity with Espresso

- TODO: refine Snow colors;

* Mobile styling fixed

- Mobile menu icon significant fixes;
- Hover only applied to desktop;
- Reorg of mixins;

* Bring Snow theme constants into parity with Espresso

- Refined Snow styles;
- Fixed missing scroll and padding in tree;
- Pane collapse button now uses proper color;
- Item Grid view refinement;
- Cleaned up code;

* Color fixes

- Super-menu description;
- Conductor time buttons hover;
- Datepicker "in-month" items color;
- Espresso colorKeyFilter brightened;
2018-10-10 12:45:46 -07:00
814b404614 Fixes for Inspector Plot properties, and more
- Fixed overflow problem when Inspector collapsed;
- Temp legacy styling for Plot inspection, series options, etc.
- Factored out non-useful gridTwoColumn mixin;
- Brought back legacy tree.scss file in legacy-styles.scss;
2018-10-05 15:13:38 -07:00
ba2bb2180b Merge branch 'topic-core-refactor' of https://github.com/nasa/openmct into topic-core-refactor 2018-10-05 12:15:30 -07:00
72cdb352f0 Fix s-selected, edit grid displays 2018-10-05 12:15:20 -07:00
cedf942c0c apply is-editing only when editing 2018-10-05 11:58:22 -07:00
27506a3757 Remove selectable from display layut 2018-10-05 11:39:41 -07:00
acc4e03c88 Merge duplicate data method, correct is-editing class 2018-10-05 11:25:30 -07:00
48 changed files with 1841 additions and 1906 deletions

View File

@ -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": [

View File

@ -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++
});

View File

@ -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();
});

View File

@ -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"
]
}
]

View File

@ -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;
}
);

View File

@ -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);
});
});
});
}
);

View File

@ -223,6 +223,8 @@ define([
*/
this.indicators = new api.IndicatorAPI(this);
this.notifications = new api.NotificationAPI();
this.Dialog = api.Dialog;
this.editor = new api.EditorAPI.default(this);

View File

@ -29,6 +29,7 @@ define([
'./ui/GestureAPI',
'./telemetry/TelemetryAPI',
'./indicators/IndicatorAPI',
'./notifications/NotificationAPI',
'./Editor'
], function (
@ -40,6 +41,7 @@ define([
GestureAPI,
TelemetryAPI,
IndicatorAPI,
NotificationAPI,
EditorAPI
) {
return {
@ -51,6 +53,7 @@ define([
GestureAPI: GestureAPI,
TelemetryAPI: TelemetryAPI,
IndicatorAPI: IndicatorAPI,
NotificationAPI: NotificationAPI.default,
EditorAPI: EditorAPI
};
});

View File

@ -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;

View 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;
}
}

View 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;
}
}
}
}

View File

@ -79,7 +79,6 @@
}
.c-grid {
z-index: -1;
pointer-events: none;
&__x { @include bgTicks($colorGridLines, 'x'); }
@ -92,13 +91,18 @@
background: rgba($editColor, 0.1);
}
.c-frame,
.l-layout {
&.s-selected,
&.s-selected-parent {
.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;
}
}
}
}
@ -310,20 +314,12 @@
}
},
mounted() {
this.removeSelectable = this.openmct.selection.selectable(
this.$el,
{
item: this.newDomainObject
},
this.initSelect
);
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.removeSelectable();
this.unlisten();
}
}

View File

@ -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>

View File

@ -1,7 +1,7 @@
<div class="c-ne__embed">
<div class="c-ne__embed__snap-thumb"
v-if="embed.snapshot"
v-on:click="openSnapshot(domainObject, entry, embed)">
v-on:click="openSnapshot">
<img v-bind:src="embed.snapshot.src">
</div>
<div class="c-ne__embed__info">

View File

@ -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)"

View File

@ -15,7 +15,7 @@
<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" v-on:click="annotateSnapshot">
<a class="s-button icon-pencil" title="Annotate">
<span class="title-label">Annotate</span>
</a>
</div>

View File

@ -23,16 +23,16 @@
define([
'moment',
'zepto',
'../utils/SnapshotOverlay',
'../../res/templates/snapshotTemplate.html',
'vue',
'painterro'
'vue'
],
function (
Moment,
$,
SnapshotOverlay,
SnapshotTemplate,
Vue,
Painterro
Vue
) {
function EmbedController (openmct, domainObject) {
this.openmct = openmct;
@ -52,102 +52,11 @@ function (
EmbedController.prototype.navigate = function (embedType) {
this.objectService.getObjects([embedType]).then(function (objects) {
this.navigationService.setNavigation(objects[embedType]);
this.navigationService.setNavigation(objects[embedType]);
}.bind(this));
};
EmbedController.prototype.openSnapshot = function (domainObject, entry, embed) {
function annotateSnapshot(openmct) {
return function () {
var save = false,
painterroInstance = {},
annotateOverlay = new Vue({
template: '<div id="snap-annotation"></div>'
}),
self = this;
var options = {
cssClass: 'l-large-view',
onDestroy: function () {
annotateOverlay.$destroy(true);
},
buttons: [
{
label: 'Cancel',
callback: function () {
save = false;
painterroInstance.save();
}
},
{
label: 'Save',
callback: function () {
save = true;
painterroInstance.save();
}
}
]
};
openmct.OverlayService.show(annotateOverlay.$mount().$el, options);
painterroInstance = Painterro({
id: 'snap-annotation',
activeColor: '#ff0000',
activeColorAlpha: 1.0,
activeFillColor: '#fff',
activeFillColorAlpha: 0.0,
backgroundFillColor: '#000',
backgroundFillColorAlpha: 0.0,
defaultFontSize: 16,
defaultLineWidth: 2,
defaultTool: 'ellipse',
hiddenTools: ['save', 'open', 'close', 'eraser', 'pixelize', 'rotate', 'settings', 'resize'],
translation: {
name: 'en',
strings: {
lineColor: 'Line',
fillColor: 'Fill',
lineWidth: 'Size',
textColor: 'Color',
fontSize: 'Size',
fontStyle: 'Style'
}
},
saveHandler: function (image, done) {
if (save) {
var entryPos = self.findInArray(domainObject.entries, entry.id),
embedPos = self.findInArray(entry.embeds, embed.id);
if (entryPos !== -1 && embedPos !== -1) {
var url = image.asBlob(),
reader = new window.FileReader();
reader.readAsDataURL(url);
reader.onloadend = function () {
var snapshot = reader.result,
snapshotObject = {
src: snapshot,
type: url.type,
size: url.size,
modified: Date.now()
},
dirString = 'entries[' + entryPos + '].embeds[' + embedPos + '].snapshot';
openmct.objects.mutate(domainObject, dirString, snapshotObject);
};
}
} else {
console.log('You cancelled the annotation!!!');
}
done(true);
}
}).show(embed.snapshot.src);
};
}
EmbedController.prototype.openSnapshot = function () {
var self = this,
snapshot = new Vue({
template: SnapshotTemplate,
@ -157,22 +66,15 @@ function (
};
},
methods: {
formatTime: self.formatTime,
annotateSnapshot: annotateSnapshot(self.openmct),
findInArray: self.findInArray
formatTime: self.formatTime
}
});
function onDestroyCallback() {
snapshot.$destroy(true);
}
var options = {
onDestroy: onDestroyCallback,
cssClass: 'l-large-view'
};
this.openmct.OverlayService.show(snapshot.$mount().$el, options);
this.openmct.OverlayService.show(snapshot.$mount().$el, {onDestroy: onDestroyCallback, cssClass: 'l-large-view'});
};
EmbedController.prototype.formatTime = function (unixTime, timeFormat) {
@ -223,20 +125,23 @@ function (
var entryPosition = self.findInArray(self.domainObject.entries, entry.id),
embedPosition = self.findInArray(entry.embeds, embed.id);
self.openmct.OverlayService.showBlockingMessage({
var warningDialog = self.dialogService.showBlockingMessage({
severity: "error",
actionText: 'This Action will permanently delete this embed. Do you wish to continue?',
buttons: [{
label: "No",
callback: function () {}
},
{
label: "Yes",
title: "This action will permanently delete this embed. Do you wish to continue?",
options: [{
label: "OK",
callback: function () {
entry.embeds.splice(embedPosition, 1);
var dirString = 'entries[' + entryPosition + '].embeds';
self.openmct.objects.mutate(self.domainObject, dirString, entry.embeds);
warningDialog.dismiss();
}
},{
label: "Cancel",
callback: function () {
warningDialog.dismiss();
}
}]
});
@ -302,8 +207,7 @@ function (
openSnapshot: self.openSnapshot,
formatTime: self.formatTime,
toggleActionMenu: self.toggleActionMenu,
actionToMenuDecorator: self.actionToMenuDecorator,
findInArray: self.findInArray
actionToMenuDecorator: self.actionToMenuDecorator
};
};

View File

@ -81,18 +81,21 @@ function (
if (entryPos !== -1) {
this.openmct.OverlayService.showBlockingMessage({
var errorDialog = this.dialogService.showBlockingMessage({
severity: "error",
actionText: "This action will permanently delete this Notebook entry. Do you wish to continue?",
buttons: [{
label: "No",
callback: function () {}
},
{
label: "Yes",
title: "This action will permanently delete this Notebook entry. Do you wish to continue?",
options: [{
label: "OK",
callback: function () {
domainObject.entries.splice(entryPos, 1);
openmct.objects.mutate(domainObject, 'entries', domainObject.entries);
errorDialog.dismiss();
}
},{
label: "Cancel",
callback: function () {
errorDialog.dismiss();
}
}]
});

View File

@ -60,7 +60,7 @@ function (
this.container = container;
var notebookEmbed = {
inject:['openmct', 'domainObject'],
inject:['openmct'],
props:['embed', 'entry'],
template: EmbedTemplate,
data: embedController.exposedData,
@ -81,7 +81,7 @@ function (
var notebookVue = Vue.extend({
template: NotebookTemplate,
provide: {openmct: self.openmct, domainObject: self.domainObject},
provide: {openmct: self.openmct},
components: {
'notebook-entry': entryComponent,
'search': search.default

View File

@ -27,65 +27,61 @@
<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"
v-if="isFixed">
<!-- Fixed input -->
<div class="c-conductor__start__fixed__label">Start</div>
<input class="c-input--datetime"
type="text" autocorrect="off" spellcheck="false"
ref="startDate"
v-model="formattedBounds.start"
@change="validateBounds('start', $event.target); setBoundsFromView()" />
<date-picker
:default-date-time="formattedBounds.start"
:formatter="timeFormatter"
@date-selected="startDateSelected"></date-picker>
</div>
<div class="c-ctrl-wrapper c-conductor-input c-conductor__start__delta"
v-if="!isFixed">
<!-- RT input -->
<div class="c-direction-indicator icon-minus"></div>
<input class="c-input--hrs-min-sec"
type="text" autocorrect="off"
spellcheck="false"
v-model="offsets.start"
@change="validateOffsets($event); setOffsetsFromView()">
</div>
<div class="c-ctrl-wrapper c-conductor-input c-conductor__start-fixed"
v-if="isFixed">
<!-- Fixed start -->
<div class="c-conductor__start-fixed__label">Start</div>
<input class="c-input--datetime"
type="text" autocorrect="off" spellcheck="false"
ref="startDate"
v-model="formattedBounds.start"
@change="validateBounds('start', $event.target); setBoundsFromView()" />
<date-picker
:default-date-time="formattedBounds.start"
:formatter="timeFormatter"
@date-selected="startDateSelected"></date-picker>
</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>
<input class="c-input--datetime"
type="text" autocorrect="off" spellcheck="false"
v-model="formattedBounds.end"
:disabled="!isFixed"
ref="endDate"
@change="validateBounds('end', $event.target); setBoundsFromView()">
<date-picker
class="c-ctrl-wrapper--menus-left"
:default-date-time="formattedBounds.end"
:formatter="timeFormatter"
@date-selected="endDateSelected"></date-picker>
</div>
<div class="c-ctrl-wrapper c-conductor-input c-conductor__start-delta"
v-if="!isFixed">
<!-- RT start -->
<div class="c-direction-indicator icon-minus"></div>
<input class="c-input--hrs-min-sec"
type="text" autocorrect="off"
spellcheck="false"
v-model="offsets.start"
@change="validateOffsets($event); setOffsetsFromView()">
</div>
<div class="c-ctrl-wrapper c-conductor-input c-conductor__end__delta"
v-if="!isFixed">
<!-- RT input -->
<div class="c-direction-indicator icon-plus"></div>
<input class="c-input--hrs-min-sec"
type="text"
autocorrect="off"
spellcheck="false"
v-model="offsets.end"
@change="validateOffsets($event); setOffsetsFromView()">
<div class="c-ctrl-wrapper c-conductor-input c-conductor__end-fixed">
<!-- Fixed end and RT 'last update' display -->
<div class="c-conductor__end-fixed__label">
{{ isFixed ? 'End' : 'Updated' }}
</div>
<input class="c-input--datetime"
type="text" autocorrect="off" spellcheck="false"
v-model="formattedBounds.end"
:disabled="!isFixed"
ref="endDate"
@change="validateBounds('end', $event.target); setBoundsFromView()">
<date-picker
class="c-ctrl-wrapper--menus-left"
:default-date-time="formattedBounds.end"
:formatter="timeFormatter"
@date-selected="endDateSelected"
v-if="isFixed"></date-picker>
</div>
<div class="c-ctrl-wrapper c-conductor-input c-conductor__end-delta"
v-if="!isFixed">
<!-- RT end -->
<div class="c-direction-indicator icon-plus"></div>
<input class="c-input--hrs-min-sec"
type="text"
autocorrect="off"
spellcheck="false"
v-model="offsets.end"
@change="validateOffsets($event); setOffsetsFromView()">
</div>
<conductor-axis
@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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'],

View File

@ -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;
}
}

View 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;
*/

View File

@ -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); // Used
$colorOvrBg: $colorBodyBg; // Used
$colorOvrFg: $colorBodyFg; // Used
$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;
*/

View File

@ -40,20 +40,20 @@ $inputTextP: $inputTextPTopBtm $inputTextPLeftRight;
$menuLineH: 1.5rem;
$treeItemIndent: 16px;
$treeTypeIconW: 18px;
$overlayOuterMarginLg: 5%;
$overlayOuterMarginDialog: 20%;
$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 */

View File

@ -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 {
@ -237,55 +128,73 @@ button {
}
/******************************************************** FORM ELEMENTS */
/********* Inline inputs */
.c-input-inline {
// A text input or contenteditable element that indicates edit affordance on hover and looks like an input on focus
@include input-base();
border: 1px solid transparent;
display: block !important;
min-width: 0;
padding-left: 0;
padding-right: 0;
overflow: hidden;
transition: all 250ms ease;
white-space: nowrap;
input, textarea {
font-family: inherit;
font-weight: inherit;
letter-spacing: inherit;
}
&:not(:focus) {
text-overflow: ellipsis;
}
&:hover,
&:focus {
padding-left: $inputTextPLeftRight;
padding-right: $inputTextPLeftRight;
}
&:hover {
border-color: rgba($colorBodyFg, 0.2);
}
&:focus {
@include nice-input($shdw: rgba(0, 0, 0, 0.6) 0 1px 3px);
border-color: transparent;
input[type=text],
input[type=search],
input[type=number] {
@include reactive-input();
padding: $inputTextP;
&.numeric {
text-align: right;
}
}
.c-labeled-input {
// An input used in the Toolbar
// Assumes label is before the input
@include cControl();
input {
margin-left: $interiorMarginSm;
}
input[type=number]::-webkit-inner-spin-button,
input[type=number]::-webkit-outer-spin-button {
margin-right: -5px !important;
margin-top: -1px !important;
}
/******************************************************** HYPERLINKS AND HYPERLINK BUTTONS */
.c-hyperlink {
&--link {
color: $colorKey;
.c-input {
&--datetime {
// Sized for values such as 2018-09-28 22:32:33.468Z
width: 160px;
}
&--button {
@include cButton();
&--hrs-min-sec {
// Sized for values such as 00:25:00
width: 60px;
}
&-inline,
&--inline {
// 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;
padding-left: 0;
padding-right: 0;
overflow: hidden;
transition: all 250ms ease;
white-space: nowrap;
&:not(:focus) {
text-overflow: ellipsis;
}
&:hover,
&:focus {
background: $colorInputBg;
padding-left: $inputTextPLeftRight;
padding-right: $inputTextPLeftRight;
}
}
&--labeled {
// TODO: replace .c-labeled-input with this
// An input used in the Toolbar
// Assumes label is before the input
@include cControl();
input {
margin-left: $interiorMarginSm;
}
}
}
@ -323,7 +232,6 @@ button {
}
@mixin menuInner() {
color: $colorMenuFg;
li {
@include cControl();
justify-content: start;
@ -349,10 +257,6 @@ button {
}
}
@mixin modalMenu() {
// Optional modifier that makes a c-menu more mobile-friendly
}
.c-menu {
@include menuOuter();
@include menuInner();
@ -397,11 +301,6 @@ button {
justify-content: stretch;
.l-item-description {
&__icon,
&__description {
//flex: 1 1 50%;
}
&__name,
&__description {
margin-top: $interiorMarginLg;
@ -413,6 +312,7 @@ button {
}
&__name {
color: $colorMenuFg;
flex: 0 0 auto;
font-size: 1.25em;
}
@ -576,3 +476,9 @@ button {
}
}
}
/***************************************************** SLIDERS */
.c-slider {
@include cControl();
> * + * { margin-left: $interiorMargin; }
}

View File

@ -49,7 +49,7 @@
font-style: normal;
}
/******************************************************** RESETS */
/******************************* RESETS */
*,
:before,
:after {
@ -60,7 +60,7 @@ div {
position: relative;
}
/******************************************************** UTILITIES */
/******************************* UTILITIES */
.u-contents {
display: contents;
}
@ -74,7 +74,7 @@ div {
}
}
/******************************************************** BROWSER ELEMENTS */
/******************************* BROWSER ELEMENTS */
body.desktop {
::-webkit-scrollbar {
box-sizing: border-box;
@ -92,30 +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;
}
}
input[type=number]::-webkit-inner-spin-button,
input[type=number]::-webkit-outer-spin-button {
//margin: -1px -5px inherit -5px !important;
margin-right: -5px !important;
margin-top: -1px !important;
}
/************************** HTML ENTITIES */
@ -223,21 +209,7 @@ body.desktop .has-local-controls {
//}
//}
/******************************************************** IS */
.is-selectable {
&:hover {
}
}
.is-editing {
.is-selectable {
}
}
/******************************************************** LEGACY */
/************************** LEGACY */
mct-container {
display: block;
@ -309,6 +281,21 @@ a.disabled {
text-align: center;
}
.no-selection {
// aka selection = "None". Used in palettes and their menu buttons.
$c: red;
$s: 48%;
$e: 52%;
background-image: linear-gradient(-45deg,
transparent $s - 5%,
$c $s,
$c $e,
transparent $e + 5%
);
background-repeat: no-repeat;
background-size: contain;
}
.scrolling,
.scroll {
overflow: auto;

View File

@ -20,8 +20,6 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
// VERSION MANUALLY MIGRATED FROM VUE-TOOLBAR
/************************** VISUALS */
@mixin ancillaryIcon($d, $c) {
// Used for small icons used in combination with larger icons,
@ -115,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"] {
border-radius: $smallCr;
background-color: $colorInspectorSectionHeaderBg;
color: $colorInspectorSectionHeaderFg;
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;
@mixin propertiesHeader() {
border-radius: $smallCr;
background-color: $colorInspectorSectionHeaderBg;
color: $colorInspectorSectionHeaderFg;
font-weight: normal;
margin: 0 0 $interiorMarginSm 0;
padding: $interiorMarginSm $interiorMargin;
text-transform: uppercase;
}
@mixin modalFullScreen() {
@ -165,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;
@ -184,7 +170,7 @@
border-radius: $controlCr;
&.error {
background-color: $colorFormFieldErrorBg;
background: $colorFormFieldErrorBg;
color: $colorFormFieldErrorFg;
}
}
@ -202,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.

View File

@ -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";

View File

@ -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 {

View File

@ -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";

View File

@ -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>
@ -61,14 +62,11 @@
// __handle and __label don't appear in mobile
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 @@
}
}
&__header {
font-size: floor(12px * .9);
}
&__collapse-button {
$m: 2px;
$h: 12px;
box-shadow: none;
background: $splitterBtnColorBg;
color: $splitterBtnColorFg;
flex: 0 0 nth($splitterBtnD, 1);
font-size: $h * .9;
position: relative;
justify-content: start;
transition: $transOut;
border-radius: $smallCr;
font-size: 6px;
line-height: 90%;
padding: 3px 15px;
&:after {
// Close icon
background: $colorBtnBg;
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 {
/************************ 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;
}
.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;
[class*="label"] {
&:before {
position: absolute;
transform: translate($interiorMarginLg + 1, 18px) rotate(90deg);
top: 0;
}
&:after {
background: none;
padding: 0;
top: $interiorMargin;
left: 50%;
right: auto;
transform: translateX(-50%);
width: auto;
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);
}
}

View File

@ -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;
}
}
}

View File

@ -75,7 +75,12 @@
this.openmct.editor.cancel();
},
saveAndFinishEditing() {
this.openmct.editor.save();
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 () {

View File

@ -1,5 +1,7 @@
<template>
<div class="l-shell" :class="{'is-editing': isEditing}">
<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,12 +25,9 @@
<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"
:class="{ 'is-editing' : true }">
<pane class="l-shell__pane-main">
<browse-bar class="l-shell__main-view-browse-bar"
ref="browseBar">
</browse-bar>
@ -48,7 +47,7 @@
</pane>
</multipane>
<div class="l-shell__status">
<MctStatus></MctStatus>
<StatusBar></StatusBar>
</div>
</div>
</template>
@ -62,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
@ -138,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*="__"] {
@ -165,27 +184,40 @@
}
/******************************* 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%;
@ -198,18 +230,12 @@
&__pane-inspector {
width: 200px;
}
&__toolbar {
flex: 0 0 auto;
margin-bottom: $interiorMargin;
}
}
}
</style>
<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';
@ -219,6 +245,7 @@
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 = () => {
@ -251,12 +278,8 @@
export default {
inject: ['openmct'],
data() {
return {isEditing: false};
},
components: {
Inspector,
MctStatus,
MctTree,
ObjectView,
'mct-template': MctTemplate,
@ -266,6 +289,7 @@
multipane,
pane,
BrowseBar,
StatusBar,
Toolbar
},
mounted() {
@ -276,7 +300,8 @@
data: function () {
return {
fullScreen: false,
conductorComponent: {}
conductorComponent: {},
isEditing: false
}
},
methods: {

View File

@ -1,14 +0,0 @@
<template>
<span class="c-status">
[ Status ]
</span>
</template>
<style lang="scss">
</style>
<script>
export default {
}
</script>

View File

@ -1,10 +1,12 @@
<template>
<ul class="c-tree">
<tree-item v-for="child in children"
:key="child.id"
:node="child">
</tree-item>
</ul>
<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;
}
@ -99,7 +106,7 @@
children: []
};
},
inject: ['openmct', 'domainObject'],
inject: ['openmct'],
mounted: function () {
this.openmct.objects.get('ROOT')
.then(root => this.openmct.composition.get(root).load())

View 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>

View 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>

View 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>

View File

@ -11,6 +11,8 @@
</template>
<script>
import ContextMenuDirective from './ContextMenuDirective.js';
export default {
data: function () {
return {

View 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(){
}
}

View 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);
}
}
}

View File

@ -1,91 +0,0 @@
<template>
<div class="c-message"
v-bind:class="">
<!-- This element is displayed within the overlay service as well as in the list of messages
Uses flex-row -->
<div class="c-message__icon"
:class="['message-severity-' + model.severity]"></div>
<div class="c-message__text">
<!-- Uses flex-column -->
<div class="c-message__title"
v-if="model.title">
{{model.title}}
</div>
<div class="c-message__hint"
v-if="model.hint">
{{model.hint}}
<span v-if="model.timestamp">[{{model.timestamp}}]</span>
</div>
<div class="c-message__action-text"
v-if="model.actionText">
{{model.actionText}}
</div>
<div class="c-message__actions"
v-if="model.primaryOption">
<a class="c-button c-button--major"
@click="model.primaryOption.callback()">
{{model.primaryOption.label}}
</a>
</div>
</div>
</div>
</template>
<style lang="scss">
@import "~styles/sass-base";
.c-message {
display: flex;
padding: $interiorMarginLg;
> * + * {
@include test();
margin-left: $interiorMarginLg;
}
&__icon {
$s: 50px;
flex: 0 0 auto;
min-width: $s;
min-height: $s;
&.message-severity {
// TEMP: TODO: replace with SVG background assets
&-alert {
background: $colorAlert;
}
&-error {
background: $colorFormError;
}
}
}
&__text {
display: flex;
flex-direction: column;
flex: 1 1 auto;
> * + * {
@include test();
margin-top: $interiorMargin;
}
}
// __text elements
&__title,
&__action-text {
font-size: 1.2em; // TEMP
}
}
</style>
<script>
export default {
inject:['model']
}
</script>

View File

@ -8,19 +8,9 @@
v-on:click="destroy">
</button>
<div class="c-overlay__contents" ref="element"></div>
<div class="c-overlay__button-bar" v-if="!buttons">
<div class="c-overlay__button-bar">
<button class="c-button c-button--major"
v-on:click="destroy">
Done
</button>
</div>
<div class="c-overlay__button-bar" v-if="buttons">
<button class="c-button c-button--major"
v-for="(button, index) in buttons"
:key="index"
@click="buttonClickHandler(button.callback)">
{{button.label}}
</button>
v-on:click="destroy">Done</button>
</div>
</div>
</div>
@ -29,13 +19,6 @@
<style lang="scss">
@import "~styles/sass-base";
@mixin overlaySizing($marginTB: 5%, $marginLR: $marginTB, $width: auto, $height: auto) {
position: absolute;
top: $marginTB; right: $marginLR; bottom: $marginTB; left: $marginLR;
width: $width;
height: $height;
}
.l-overlay-wrapper {
// Created by overlayService.js, contains this template.
// Acts as an anchor for one or more overlays.
@ -52,8 +35,8 @@
&__outer {
@include abs();
background: $colorOvrBg;
color: $colorOvrFg;
background: $overlayColorBg;
color: $overlayColorFg;
display: flex;
flex-direction: column;
padding: $overlayInnerMargin;
@ -79,77 +62,30 @@
margin-top: $interiorMargin;
}
// Overlay types, styling independent of platform.
.l-large-view & {
// Default
}
.l-dialog & {
//
}
.l-message & {
&__outer {
// background: orange;
}
}
}
body.desktop {
.c-overlay {
body.desktop & {
&__blocker {
@include abs();
background: $colorOvrBlocker;
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;
// Defaults to l-large-view
@include overlaySizing($overlayOuterMarginLg);
}
}
// Overlay types, styling for desktop.
.l-large-view {
// Default
}
.l-dialog {
.c-overlay__outer {
@include overlaySizing($overlayOuterMarginDialog);
}
}
.l-message {
.c-overlay__outer {
@include overlaySizing(auto);
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}
}
</style>
<script>
export default {
inject: ['destroy', 'element', 'buttons'],
inject: ['destroy', 'element'],
mounted() {
this.$refs.element.appendChild(this.element);
},
methods: {
buttonClickHandler: function (method) {
method();
this.destroy();
}
}
}
</script>

View File

@ -22,19 +22,15 @@
define([
'./overlay.vue',
'./blockingMessage.vue',
'vue'
], function (
OverlayComponent,
BlockingMessage,
Vue
) {
function OverlayService() {
this.activeOverlays = [];
this.overlayId = 0;
this.showBlockingMessage = this.showBlockingMessage.bind(this);
}
OverlayService.prototype.show = function (element, options) {
@ -47,96 +43,45 @@ define([
component = new Vue({
provide: {
destroy: this.destroy.bind(this),
element: element,
buttons: options.buttons
element: element
},
components: {
OverlayComponent: OverlayComponent.default
},
template: '<overlay-component></overlay-component>'
}),
dialog = {};
});
overlay.classList.add('l-overlay-wrapper', overlayTypeCssClass);
document.body.appendChild(overlay);
overlay.appendChild(component.$mount().$el);
var overlayObject = {
this.activeOverlays.push({
overlay: overlay,
component: component,
onDestroy: options.onDestroy,
id: this.overlayId,
dialog: dialog
};
id: this.overlayId
});
dialog.dismiss = function () {
let pos = findInArray(overlayObject.id, this.activeOverlays);
if (pos !== -1) {
if (overlayObject.onDestroy && typeof overlayObject.onDestroy === 'function') {
overlayObject.onDestroy();
}
overlayObject.component.$destroy(true);
document.body.removeChild(overlayObject.overlay);
this.activeOverlays.splice(pos, 1);
if (this.activeOverlays.length) {
this.activeOverlays[this.activeOverlays.length - 1].overlay.classList.remove('invisible');
}
}
}.bind(this);
this.activeOverlays.push(overlayObject);
this.overlayId++;
return dialog;
};
OverlayService.prototype.destroy = function () {
var lastActiveOverlayObject = this.activeOverlays[this.activeOverlays.length - 1];
var lastActiveOverlayObject = this.activeOverlays.pop(),
lastActiveOverlay = lastActiveOverlayObject.overlay,
lastActiveComponent = lastActiveOverlayObject.component;
lastActiveOverlayObject.dialog.dismiss(lastActiveOverlayObject.id);
};
OverlayService.prototype.showBlockingMessage = function (model) {
let component = new Vue({
provide: {
model: model
},
components: {
BlockingMessage: BlockingMessage.default
},
template: '<blocking-message></blocking-message>'
});
function destroy() {
component.$destroy(true);
if (lastActiveOverlayObject.onDestroy && typeof lastActiveOverlayObject.onDestroy === 'function') {
lastActiveOverlayObject.onDestroy();
}
let options = {
cssClass: 'l-message',
onDestroy: destroy,
buttons: model.buttons
};
lastActiveComponent.$destroy(true);
document.body.removeChild(lastActiveOverlay);
return this.show(component.$mount().$el, options);
if (this.activeOverlays.length) {
this.activeOverlays[this.activeOverlays.length - 1].overlay.classList.remove('invisible');
}
};
function findInArray(id, array) {
var found = -1;
array.forEach(function (o,i) {
if (o.id === id) {
found = i;
return;
}
});
return found;
}
return OverlayService;
});