diff --git a/bundles.json b/bundles.json index a0f7edd7a8..291553ba11 100644 --- a/bundles.json +++ b/bundles.json @@ -6,10 +6,12 @@ "platform/commonUI/browse", "platform/commonUI/edit", "platform/commonUI/dialog", + "platform/commonUI/formats", "platform/commonUI/general", "platform/commonUI/inspect", "platform/commonUI/mobile", "platform/commonUI/themes/espresso", + "platform/commonUI/notification", "platform/containment", "platform/execution", "platform/telemetry", diff --git a/example/generator/bundle.json b/example/generator/bundle.json index cdb4736957..7cf1c7b6f2 100644 --- a/example/generator/bundle.json +++ b/example/generator/bundle.json @@ -16,6 +16,23 @@ "implementation": "SinewaveLimitCapability.js" } ], + "formats": [ + { + "key": "example.delta", + "implementation": "SinewaveDeltaFormat.js" + } + ], + "constants": [ + { + "key": "TIME_CONDUCTOR_DOMAINS", + "value": [ + { "key": "time", "name": "Time" }, + { "key": "yesterday", "name": "Yesterday" }, + { "key": "delta", "name": "Delta", "format": "example.delta" } + ], + "priority": -1 + } + ], "types": [ { "key": "generator", @@ -38,6 +55,11 @@ { "key": "yesterday", "name": "Yesterday" + }, + { + "key": "delta", + "name": "Delta", + "format": "example.delta" } ], "ranges": [ diff --git a/platform/commonUI/general/res/sass/_hide-non-functional.scss b/example/generator/src/SinewaveConstants.js similarity index 80% rename from platform/commonUI/general/res/sass/_hide-non-functional.scss rename to example/generator/src/SinewaveConstants.js index 14860ec8ce..29136ebb99 100644 --- a/platform/commonUI/general/res/sass/_hide-non-functional.scss +++ b/example/generator/src/SinewaveConstants.js @@ -19,18 +19,8 @@ * this source code distribution or the Licensing information page available * at runtime from the About dialog for additional information. *****************************************************************************/ -// Styles to temporarily hide non-functional elements +/*global define,Promise*/ -/******************************** BROWSE */ -.browse-mode { - .browse { - &.top-bar { - display: none; - } - } - - .browse-area.holder { - // When .browse.top-bar is hidden, set the top of the browse-area holder - top: $bodyMargin; - } -} \ No newline at end of file +define({ + START_TIME: Date.now() - 24 * 60 * 60 * 1000 // Now minus a day. +}); diff --git a/example/generator/src/SinewaveDeltaFormat.js b/example/generator/src/SinewaveDeltaFormat.js new file mode 100644 index 0000000000..19f3e631f9 --- /dev/null +++ b/example/generator/src/SinewaveDeltaFormat.js @@ -0,0 +1,68 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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 define,Promise*/ + +define( + ['./SinewaveConstants', 'moment'], + function (SinewaveConstants, moment) { + "use strict"; + + var START_TIME = SinewaveConstants.START_TIME, + FORMAT_REGEX = /^-?\d+:\d+:\d+$/, + SECOND = 1000, + MINUTE = SECOND * 60, + HOUR = MINUTE * 60; + + function SinewaveDeltaFormat() { + } + + function twoDigit(v) { + return v >= 10 ? String(v) : ('0' + v); + } + + SinewaveDeltaFormat.prototype.format = function (value) { + var delta = Math.abs(value - START_TIME), + negative = value < START_TIME, + seconds = Math.floor(delta / SECOND) % 60, + minutes = Math.floor(delta / MINUTE) % 60, + hours = Math.floor(delta / HOUR); + return (negative ? "-" : "") + + [ hours, minutes, seconds ].map(twoDigit).join(":"); + }; + + SinewaveDeltaFormat.prototype.validate = function (text) { + return FORMAT_REGEX.test(text); + }; + + SinewaveDeltaFormat.prototype.parse = function (text) { + var negative = text[0] === "-", + parts = text.replace("-", "").split(":"); + return [ HOUR, MINUTE, SECOND ].map(function (sz, i) { + return parseInt(parts[i], 10) * sz; + }).reduce(function (a, b) { + return a + b; + }, 0) * (negative ? -1 : 1) + START_TIME; + }; + + return SinewaveDeltaFormat; + } +); diff --git a/example/generator/src/SinewaveTelemetrySeries.js b/example/generator/src/SinewaveTelemetrySeries.js index 1e84034766..fa47f8f59a 100644 --- a/example/generator/src/SinewaveTelemetrySeries.js +++ b/example/generator/src/SinewaveTelemetrySeries.js @@ -25,12 +25,12 @@ * Module defining SinewaveTelemetry. Created by vwoeltje on 11/12/14. */ define( - [], - function () { + ['./SinewaveConstants'], + function (SinewaveConstants) { "use strict"; var ONE_DAY = 60 * 60 * 24, - firstObservedTime = Math.floor(Date.now() / 1000) - ONE_DAY; + firstObservedTime = Math.floor(SinewaveConstants.START_TIME / 1000); /** * @@ -58,6 +58,9 @@ define( }; generatorData.getDomainValue = function (i, domain) { + // delta uses the same numeric values as the default domain, + // so it's not checked for here, just formatted for display + // differently. return (i + offset) * 1000 + firstTime * 1000 - (domain === 'yesterday' ? ONE_DAY : 0); }; diff --git a/example/notifications/bundle.json b/example/notifications/bundle.json new file mode 100644 index 0000000000..bb2d464d64 --- /dev/null +++ b/example/notifications/bundle.json @@ -0,0 +1,47 @@ +{ + "extensions": { + "templates": [ + { + "key": "dialogLaunchTemplate", + "templateUrl": "dialog-launch.html" + }, + { + "key": "notificationLaunchTemplate", + "templateUrl": "notification-launch.html" + } + ], + "controllers": [ + { + "key": "DialogLaunchController", + "implementation": "DialogLaunchController.js", + "depends": [ + "$scope", + "$timeout", + "$log", + "dialogService", + "notificationService" + ] + }, + { + "key": "NotificationLaunchController", + "implementation": "NotificationLaunchController.js", + "depends": [ + "$scope", + "$timeout", + "$log", + "notificationService" + ] + } + ], + "indicators": [ + { + "implementation": "DialogLaunchIndicator.js", + "priority": "fallback" + }, + { + "implementation": "NotificationLaunchIndicator.js", + "priority": "fallback" + } + ] + } +} diff --git a/example/notifications/res/dialog-launch.html b/example/notifications/res/dialog-launch.html new file mode 100644 index 0000000000..bc56e6b4f2 --- /dev/null +++ b/example/notifications/res/dialog-launch.html @@ -0,0 +1,10 @@ + + + + Known | + Unknown | + Error | + Info + + Dialogs + \ No newline at end of file diff --git a/example/notifications/res/notification-launch.html b/example/notifications/res/notification-launch.html new file mode 100644 index 0000000000..1e077bf3be --- /dev/null +++ b/example/notifications/res/notification-launch.html @@ -0,0 +1,10 @@ + + + + Success | + Error | + Alert | + Progress + + Notifications + \ No newline at end of file diff --git a/example/notifications/src/DialogLaunchController.js b/example/notifications/src/DialogLaunchController.js new file mode 100644 index 0000000000..f35d008cd0 --- /dev/null +++ b/example/notifications/src/DialogLaunchController.js @@ -0,0 +1,150 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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 define*/ + +define( + [], + function () { + "use strict"; + + /** + * A controller for the dialog launch view. This view allows manual + * launching of dialogs for demonstration and testing purposes. It + * also demonstrates the use of the DialogService. + * @param $scope + * @param $timeout + * @param $log + * @param dialogService + * @param notificationService + * @constructor + */ + function DialogLaunchController($scope, $timeout, $log, dialogService, notificationService) { + + /* + Demonstrates launching a progress dialog and updating it + periodically with the progress of an ongoing process. + */ + $scope.launchProgress = function (knownProgress) { + var model = { + title: "Progress Dialog Example", + progress: 0, + hint: "Do not navigate away from this page or close this browser tab while this operation is in progress.", + actionText: "Calculating...", + unknownProgress: !knownProgress, + unknownDuration: false, + severity: "info", + options: [ + { + label: "Cancel Operation", + callback: function () { + $log.debug("Operation cancelled"); + dialogService.dismiss(); + } + }, + { + label: "Do something else...", + callback: function () { + $log.debug("Something else pressed"); + } + } + ] + }; + + function incrementProgress() { + model.progress = Math.min(100, Math.floor(model.progress + Math.random() * 30)); + model.progressText = ["Estimated time remaining: about ", 60 - Math.floor((model.progress / 100) * 60), " seconds"].join(" "); + if (model.progress < 100) { + $timeout(incrementProgress, 1000); + } + } + + if (dialogService.showBlockingMessage(model)) { + //Do processing here + model.actionText = "Processing 100 objects..."; + if (knownProgress) { + $timeout(incrementProgress, 1000); + } + } else { + $log.error("Could not display modal dialog"); + } + }; + + + /* + Demonstrates launching an error dialog + */ + $scope.launchError = function () { + var model = { + title: "Error Dialog Example", + actionText: "Something happened, and it was not good.", + severity: "error", + options: [ + { + label: "Try Again", + callback: function () { + $log.debug("Try Again Pressed"); + dialogService.dismiss(); + } + }, + { + label: "Cancel", + callback: function () { + $log.debug("Cancel Pressed"); + dialogService.dismiss(); + } + } + ] + }; + + if (!dialogService.showBlockingMessage(model)) { + $log.error("Could not display modal dialog"); + } + }; + + /* + Demonstrates launching an error dialog + */ + $scope.launchInfo = function () { + var model = { + title: "Info Dialog Example", + actionText: "This is an example of a blocking info" + + " dialog. This dialog can be used to draw the user's" + + " attention to an event.", + severity: "info", + primaryOption: { + label: "OK", + callback: function () { + $log.debug("OK Pressed"); + dialogService.dismiss(); + } + } + }; + + if (!dialogService.showBlockingMessage(model)) { + $log.error("Could not display modal dialog"); + } + }; + + } + return DialogLaunchController; + } +); diff --git a/example/notifications/src/DialogLaunchIndicator.js b/example/notifications/src/DialogLaunchIndicator.js new file mode 100644 index 0000000000..9330ce2194 --- /dev/null +++ b/example/notifications/src/DialogLaunchIndicator.js @@ -0,0 +1,56 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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 define,window*/ + +define( + [], + function () { + "use strict"; + + /** + * A tool for manually invoking dialogs. When included this + * indicator will allow for dialogs of different types to be + * launched for demonstration and testing purposes. + * @constructor + */ + function DialogLaunchIndicator() { + + } + + DialogLaunchIndicator.template = 'dialogLaunchTemplate'; + + DialogLaunchIndicator.prototype.getGlyph = function () { + return "i"; + }; + DialogLaunchIndicator.prototype.getGlyphClass = function () { + return 'caution'; + }; + DialogLaunchIndicator.prototype.getText = function () { + return "Launch test dialog"; + }; + DialogLaunchIndicator.prototype.getDescription = function () { + return "Launch test dialog"; + }; + + return DialogLaunchIndicator; + } +); diff --git a/example/notifications/src/NotificationLaunchController.js b/example/notifications/src/NotificationLaunchController.js new file mode 100644 index 0000000000..851e0575f5 --- /dev/null +++ b/example/notifications/src/NotificationLaunchController.js @@ -0,0 +1,172 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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 define*/ + +define( + [], + function () { + "use strict"; + + /** + * Allows launching of notification messages for the purposes of + * demonstration and testing. Also demonstrates use of + * the NotificationService. Notifications are non-blocking messages that + * appear at the bottom of the screen to inform the user of events + * in a non-intrusive way. For more information see the + * {@link NotificationService} + * @param $scope + * @param $timeout + * @param $log + * @param notificationService + * @constructor + */ + function NotificationLaunchController($scope, $timeout, $log, notificationService) { + var messageCounter = 1; + + function getExampleActionText() { + var actionTexts = [ + "Adipiscing turpis mauris in enim elementu hac, enim aliquam etiam.", + "Eros turpis, pulvinar turpis eros eu", + "Lundium nascetur a, lectus montes ac, parturient in natoque, duis risus risus pulvinar pid rhoncus, habitasse auctor natoque!" + ]; + 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(){ + + 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()}); + }; + /** + * Launch a new notification with a severity of 'Alert'. + */ + $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()}); + }; + + + /** + * Launch a new notification with a progress bar that is updated + * periodically, tracking an ongoing process. + */ + $scope.newProgress = function(){ + + var notificationModel = { + title: "Progress notification example", + severity: "info", + progress: 0, + actionText: getExampleActionText(), + unknownProgress: false + }; + + /** + * Simulate an ongoing process and update the progress bar. + * @param notification + */ + function incrementProgress(notificationModel) { + 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); + } + } + + notificationService.notify(notificationModel); + incrementProgress(notificationModel); + }; + + /** + * Launch a new notification with severity level of INFO. + */ + $scope.newInfo = function(){ + + notificationService.info({ + title: "Example Info notification " + messageCounter++ + }); + }; + + } + return NotificationLaunchController; + } +); diff --git a/example/notifications/src/NotificationLaunchIndicator.js b/example/notifications/src/NotificationLaunchIndicator.js new file mode 100644 index 0000000000..174810d721 --- /dev/null +++ b/example/notifications/src/NotificationLaunchIndicator.js @@ -0,0 +1,50 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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 define,window*/ + +define( + [], + function () { + "use strict"; + + function NotificationLaunchIndicator() { + + } + + NotificationLaunchIndicator.template = 'notificationLaunchTemplate'; + + NotificationLaunchIndicator.prototype.getGlyph = function () { + return "i"; + }; + NotificationLaunchIndicator.prototype.getGlyphClass = function () { + return 'caution'; + }; + NotificationLaunchIndicator.prototype.getText = function () { + return "Launch notification"; + }; + NotificationLaunchIndicator.prototype.getDescription = function () { + return "Launch notification"; + }; + + return NotificationLaunchIndicator; + } +); diff --git a/platform/commonUI/browse/bundle.json b/platform/commonUI/browse/bundle.json index bc42a33c1f..9e7772c66d 100644 --- a/platform/commonUI/browse/bundle.json +++ b/platform/commonUI/browse/bundle.json @@ -31,10 +31,10 @@ ] }, { - "key": "BrowseTreeController", - "implementation": "BrowseTreeController.js", + "key": "PaneController", + "implementation": "PaneController.js", "priority": "preferred", - "depends": [ "$scope", "agentService" ] + "depends": [ "$scope", "agentService","$window" ] }, { "key": "BrowseObjectController", diff --git a/platform/commonUI/browse/res/templates/browse-object.html b/platform/commonUI/browse/res/templates/browse-object.html index 3bd6138da6..9dc1c8f83a 100644 --- a/platform/commonUI/browse/res/templates/browse-object.html +++ b/platform/commonUI/browse/res/templates/browse-object.html @@ -41,7 +41,6 @@ - diff --git a/platform/commonUI/browse/res/templates/browse.html b/platform/commonUI/browse/res/templates/browse.html index 9a1be7e771..cd6d01036d 100644 --- a/platform/commonUI/browse/res/templates/browse.html +++ b/platform/commonUI/browse/res/templates/browse.html @@ -22,22 +22,27 @@
-
- +
+
-
+ + - + -
-
- - +
+ + +
+ + +
+ + +
+ + + +
+ + + +
+
-
m
diff --git a/platform/commonUI/browse/res/templates/browse/object-header.html b/platform/commonUI/browse/res/templates/browse/object-header.html index 7ea43a6b9f..79fca1b76c 100644 --- a/platform/commonUI/browse/res/templates/browse/object-header.html +++ b/platform/commonUI/browse/res/templates/browse/object-header.html @@ -19,7 +19,7 @@ this source code distribution or the Licensing information page available at runtime from the About dialog for additional information. --> -
+
{{type.getGlyph()}} diff --git a/platform/commonUI/browse/res/templates/create/create-button.html b/platform/commonUI/browse/res/templates/create/create-button.html index a7b4ad96e5..663f8e3172 100644 --- a/platform/commonUI/browse/res/templates/create/create-button.html +++ b/platform/commonUI/browse/res/templates/create/create-button.html @@ -19,13 +19,12 @@ this source code distribution or the Licensing information page available at runtime from the About dialog for additional information. --> - \ No newline at end of file + \ No newline at end of file diff --git a/platform/commonUI/browse/res/templates/items/grid-item.html b/platform/commonUI/browse/res/templates/items/grid-item.html index 647d8e26da..426f354774 100644 --- a/platform/commonUI/browse/res/templates/items/grid-item.html +++ b/platform/commonUI/browse/res/templates/items/grid-item.html @@ -26,14 +26,8 @@
O
-
-
- {{type.getGlyph()}} - -
+
+ {{type.getGlyph()}}
}
diff --git a/platform/commonUI/browse/src/BrowseController.js b/platform/commonUI/browse/src/BrowseController.js index e0c00d9521..8c032f7de3 100644 --- a/platform/commonUI/browse/src/BrowseController.js +++ b/platform/commonUI/browse/src/BrowseController.js @@ -153,7 +153,6 @@ define( $scope.$on("$destroy", function () { navigationService.removeListener(setNavigation); }); - } return BrowseController; diff --git a/platform/commonUI/browse/src/BrowseTreeController.js b/platform/commonUI/browse/src/PaneController.js similarity index 83% rename from platform/commonUI/browse/src/BrowseTreeController.js rename to platform/commonUI/browse/src/PaneController.js index 31875de4fa..6a59baa0e0 100644 --- a/platform/commonUI/browse/src/BrowseTreeController.js +++ b/platform/commonUI/browse/src/PaneController.js @@ -33,10 +33,12 @@ define( * @constructor * @memberof platform/commonUI/browse */ - function BrowseTreeController($scope, agentService) { + function PaneController($scope, agentService, $window) { var self = this; this.agentService = agentService; - this.state = true; + + // Fast and cheap: if this has been opened in a new window, hide panes by default + this.state = !$window.opener; /** * Callback to invoke when any selection occurs in the tree. @@ -44,7 +46,7 @@ define( * to the tree representation. * * @property {Function} callback - * @memberof platform/commonUI/browse.BrowseTreeController# + * @memberof platform/commonUI/browse.PaneController# */ this.callback = function () { // Note that, since this is a callback to pass, this is not @@ -59,20 +61,20 @@ define( } /** - * Toggle the visibility of the tree. + * Toggle the visibility of the pane. */ - BrowseTreeController.prototype.toggle = function () { + PaneController.prototype.toggle = function () { this.state = !this.state; }; /** - * Get the desired visibility state of the tree. + * Get the desired visibility state of the pane. * @returns {boolean} true when visible */ - BrowseTreeController.prototype.visible = function () { + PaneController.prototype.visible = function () { return this.state; }; - return BrowseTreeController; + return PaneController; } ); diff --git a/platform/commonUI/browse/test/BrowseTreeControllerSpec.js b/platform/commonUI/browse/test/PaneControllerSpec.js similarity index 91% rename from platform/commonUI/browse/test/BrowseTreeControllerSpec.js rename to platform/commonUI/browse/test/PaneControllerSpec.js index 077855febf..f02da713a4 100644 --- a/platform/commonUI/browse/test/BrowseTreeControllerSpec.js +++ b/platform/commonUI/browse/test/PaneControllerSpec.js @@ -22,22 +22,24 @@ /*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/ define( - ["../src/BrowseTreeController"], - function (BrowseTreeController) { + ["../src/PaneController"], + function (PaneController) { 'use strict'; - describe("The BrowseTreeController", function () { + describe("The PaneController", function () { var mockScope, mockAgentService, mockDomainObjects, + mockWindow, controller; // We want to reinstantiate for each test case // because device state can influence constructor-time behavior function instantiateController() { - return new BrowseTreeController( + return new PaneController( mockScope, - mockAgentService + mockAgentService, + mockWindow ); } @@ -58,6 +60,7 @@ define( "agentService", [ "isMobile", "isPhone", "isTablet", "isPortrait", "isLandscape" ] ); + mockWindow = jasmine.createSpyObj("$window", ["open"]); }); it("is initially visible", function () { diff --git a/platform/commonUI/browse/test/suite.json b/platform/commonUI/browse/test/suite.json index aa73dd358d..b9292b6ef1 100644 --- a/platform/commonUI/browse/test/suite.json +++ b/platform/commonUI/browse/test/suite.json @@ -1,7 +1,7 @@ [ "BrowseController", "BrowseObjectController", - "BrowseTreeController", + "PaneController", "MenuArrowController", "creation/CreateAction", "creation/CreateActionProvider", diff --git a/platform/commonUI/dialog/bundle.json b/platform/commonUI/dialog/bundle.json index 9a2d541419..80cd456c20 100644 --- a/platform/commonUI/dialog/bundle.json +++ b/platform/commonUI/dialog/bundle.json @@ -24,6 +24,18 @@ { "key": "form-dialog", "templateUrl": "templates/dialog.html" + }, + { + "key": "overlay-blocking-message", + "templateUrl": "templates/overlay-blocking-message.html" + }, + { + "key": "message", + "templateUrl": "templates/message.html" + }, + { + "key": "overlay-message-list", + "templateUrl": "templates/overlay-message-list.html" } ], "containers": [ diff --git a/platform/commonUI/dialog/res/templates/dialog.html b/platform/commonUI/dialog/res/templates/dialog.html index e3379b034e..2fe2411875 100644 --- a/platform/commonUI/dialog/res/templates/dialog.html +++ b/platform/commonUI/dialog/res/templates/dialog.html @@ -21,17 +21,13 @@ -->
{{ngModel.title}}
-
- All fields marked * are required. -
+
All fields marked * are required.
-
-
- - -
+
+ +
\ No newline at end of file diff --git a/platform/commonUI/dialog/res/templates/overlay-blocking-message.html b/platform/commonUI/dialog/res/templates/overlay-blocking-message.html new file mode 100644 index 0000000000..17fdcf152c --- /dev/null +++ b/platform/commonUI/dialog/res/templates/overlay-blocking-message.html @@ -0,0 +1,25 @@ + + + + + \ No newline at end of file diff --git a/platform/commonUI/dialog/res/templates/overlay-message-list.html b/platform/commonUI/dialog/res/templates/overlay-message-list.html new file mode 100644 index 0000000000..026ef1cbb1 --- /dev/null +++ b/platform/commonUI/dialog/res/templates/overlay-message-list.html @@ -0,0 +1,19 @@ + +
+
+
{{ngModel.dialog.title}}
+
Displaying {{ngModel.dialog.messages.length}} messages +
+
+
+ +
+ +
+
\ No newline at end of file diff --git a/platform/commonUI/dialog/res/templates/overlay-options.html b/platform/commonUI/dialog/res/templates/overlay-options.html index 2f572e20a5..7de75df31c 100644 --- a/platform/commonUI/dialog/res/templates/overlay-options.html +++ b/platform/commonUI/dialog/res/templates/overlay-options.html @@ -24,13 +24,11 @@
{{ngModel.dialog.title}}
{{ngModel.dialog.hint}}
-
-
- - -
+
+ +
- - x - -
- -
+ class="clk-icon icon ui-symbol close">x +
\ No newline at end of file diff --git a/platform/commonUI/dialog/src/DialogService.js b/platform/commonUI/dialog/src/DialogService.js index 25e8943c06..27ffa9ae8b 100644 --- a/platform/commonUI/dialog/src/DialogService.js +++ b/platform/commonUI/dialog/src/DialogService.js @@ -55,7 +55,7 @@ define( this.dialogVisible = false; }; - DialogService.prototype.getDialogResponse = function (key, model, resultGetter) { + DialogService.prototype.getDialogResponse = function (key, model, resultGetter, typeClass) { // We will return this result as a promise, because user // input is asynchronous. var deferred = this.$q.defer(), @@ -84,27 +84,20 @@ define( model.confirm = confirm; model.cancel = cancel; - if (this.dialogVisible) { - // Only one dialog should be shown at a time. - // The application design should be such that - // we never even try to do this. - this.$log.warn([ - "Dialog already showing; ", - "unable to show ", - model.name - ].join("")); - deferred.reject(); - } else { + if (this.canShowDialog(model)) { // Add the overlay using the OverlayService, which // will handle actual insertion into the DOM this.overlay = this.overlayService.createOverlay( key, - model + model, + typeClass || "t-dialog" ); // Track that a dialog is already visible, to // avoid spawning multiple dialogs at once. this.dialogVisible = true; + } else { + deferred.reject(); } return deferred.promise; @@ -157,6 +150,99 @@ define( ); }; + /** + * Tests if a dialog can be displayed. A modal dialog may only be + * displayed if one is not already visible. + * Will log a warning message if it can't display a dialog. + * @returns {boolean} true if dialog is currently visible, false + * otherwise + */ + DialogService.prototype.canShowDialog = function(dialogModel){ + if (this.dialogVisible){ + // Only one dialog should be shown at a time. + // The application design should be such that + // we never even try to do this. + this.$log.warn([ + "Dialog already showing; ", + "unable to show ", + dialogModel.title + ].join("")); + + return false; + } else { + return true; + } + }; + + /** + * A user action that can be performed from a blocking dialog. These + * actions will be rendered as buttons within a blocking dialog. + * + * @typedef DialogOption + * @property {string} label a label to be displayed as the button + * text for this action + * @property {function} action a function to be called when the + * button is clicked + */ + + /** + * A description of the model options that may be passed to the + * showBlockingMessage method. Note that the DialogModel desribed + * here is shared with the Notifications framework. + * @see NotificationService + * + * @typedef DialogModel + * @property {string} title the title to use for the dialog + * @property {string} severity the severity level of this message. + * These are defined in a bundle constant with key 'dialogSeverity' + * @property {string} hint the 'hint' message to show below the title + * @property {string} actionText text that indicates a current action, + * shown above a progress bar to indicate what's happening. + * @property {number} progress a percentage value (1-100) + * indicating the completion of the blocking task + * @property {string} progressText the message to show below a + * progress bar to indicate progress. For example, this might be + * used to indicate time remaining, or items still to process. + * @property {boolean} unknownProgress some tasks may be + * impossible to provide an estimate for. Providing a true value for + * this attribute will indicate to the user that the progress and + * duration cannot be estimated. + * @property {DialogOption} primaryOption an action that will + * be added to the dialog as a button. The primary action can be + * used as the suggested course of action for the user. Making it + * distinct from other actions allows it to be styled differently, + * and treated preferentially in banner mode. + * @property {DialogOption[]} options a list of actions that will + * be added to the dialog as buttons. + */ + + /** + * Displays a blocking (modal) dialog. This dialog can be used for + * displaying messages that require the user's + * immediate attention. The message may include an indication of + * progress, as well as a series of actions that + * the user can take if necessary + * @param {DialogModel} dialogModel defines options for the dialog + * @param {typeClass} string tells overlayService that this overlay should use appropriate CSS class + * @returns {boolean} + */ + DialogService.prototype.showBlockingMessage = function(dialogModel) { + if (this.canShowDialog(dialogModel)) { + // Add the overlay using the OverlayService, which + // will handle actual insertion into the DOM + this.overlay = this.overlayService.createOverlay( + "overlay-blocking-message", + dialogModel, + "t-dialog-sm" + ); + // Track that a dialog is already visible, to + // avoid spawning multiple dialogs at once. + this.dialogVisible = true; + return true; + } else { + return false; + } + }; return DialogService; } diff --git a/platform/commonUI/dialog/src/OverlayService.js b/platform/commonUI/dialog/src/OverlayService.js index 5faba5dcf6..5e9703cb42 100644 --- a/platform/commonUI/dialog/src/OverlayService.js +++ b/platform/commonUI/dialog/src/OverlayService.js @@ -28,7 +28,7 @@ define( // Template to inject into the DOM to show the dialog; really just points to // the a specific template that can be included via mct-include - var TEMPLATE = ''; + var TEMPLATE = ''; /** @@ -71,8 +71,11 @@ define( * @param {object} overlayModel the model to pass to the * included overlay template (this will be passed * in via ng-model) + * @param {string} typeClass the element class to use in rendering + * the overlay. Can be specified to provide custom styling of + * overlays */ - OverlayService.prototype.createOverlay = function (key, overlayModel) { + OverlayService.prototype.createOverlay = function (key, overlayModel, typeClass) { // Create a new scope for this overlay var scope = this.newScope(), element; @@ -90,6 +93,7 @@ define( // Populate the scope; will be passed directly to the template scope.overlay = overlayModel; scope.key = key; + scope.typeClass = typeClass || 't-dialog'; // Create the overlay element and add it to the document's body element = this.$compile(TEMPLATE)(scope); diff --git a/platform/commonUI/dialog/test/DialogServiceSpec.js b/platform/commonUI/dialog/test/DialogServiceSpec.js index 689979f129..2dc109ac44 100644 --- a/platform/commonUI/dialog/test/DialogServiceSpec.js +++ b/platform/commonUI/dialog/test/DialogServiceSpec.js @@ -116,10 +116,22 @@ define( dialog: dialogModel, confirm: jasmine.any(Function), cancel: jasmine.any(Function) - } + }, + 't-dialog' + ); + }); + + it("invokes the overlay service with the correct parameters when" + + " a blocking dialog is requested", function() { + var dialogModel = {}; + expect(dialogService.showBlockingMessage(dialogModel)).toBe(true); + expect(mockOverlayService.createOverlay).toHaveBeenCalledWith( + "overlay-blocking-message", + dialogModel, + "t-dialog-sm" ); }); }); } -); \ No newline at end of file +); diff --git a/platform/commonUI/formats/bundle.json b/platform/commonUI/formats/bundle.json new file mode 100644 index 0000000000..99925657b2 --- /dev/null +++ b/platform/commonUI/formats/bundle.json @@ -0,0 +1,26 @@ +{ + "name": "Time services bundle", + "description": "Defines interfaces and provides default implementations for handling different time systems.", + "extensions": { + "components": [ + { + "provides": "formatService", + "type": "provider", + "implementation": "FormatProvider.js", + "depends": [ "formats[]" ] + } + ], + "formats": [ + { + "key": "utc", + "implementation": "UTCTimeFormat.js" + } + ], + "constants": [ + { + "key": "DEFAULT_TIME_FORMAT", + "value": "utc" + } + ] + } +} diff --git a/platform/commonUI/formats/src/FormatProvider.js b/platform/commonUI/formats/src/FormatProvider.js new file mode 100644 index 0000000000..e6d38fbcee --- /dev/null +++ b/platform/commonUI/formats/src/FormatProvider.js @@ -0,0 +1,114 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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 define*/ + +define([ + +], function ( + +) { + "use strict"; + + /** + * An object used to convert between numeric values and text values, + * typically used to display these values to the user and to convert + * user input to a numeric format, particularly for time formats. + * @interface {Format} + */ + + /** + * Parse text (typically user input) to a numeric value. + * Behavior is undefined when the text cannot be parsed; + * `validate` should be called first if the text may be invalid. + * @method parse + * @memberof Format# + * @param {string} text the text to parse + * @returns {number} the parsed numeric value + */ + + /** + * Determine whether or not some text (typically user input) can + * be parsed to a numeric value by this format. + * @method validate + * @memberof Format# + * @param {string} text the text to parse + * @returns {boolean} true if the text can be parsed + */ + + /** + * Convert a numeric value to a text value for display using + * this format. + * @method format + * @memberof Format# + * @param {number} value the numeric value to format + * @returns {string} the text representation of the value + */ + + /** + * Provides access to `Format` objects which can be used to + * convert values between human-readable text and numeric + * representations. + * @interface FormatService + */ + + /** + * Look up a format by its symbolic identifier. + * @method getFormat + * @memberof FormatService# + * @param {string} key the identifier for this format + * @returns {Format} the format + * @throws {Error} errors when the requested format is unrecognized + */ + + /** + * Provides formats from the `formats` extension category. + * @constructor + * @implements {FormatService} + * @memberof platform/commonUI/formats + * @param {Array.} format constructors, + * from the `formats` extension category. + */ + function FormatProvider(formats) { + var formatMap = {}; + + function addToMap(Format) { + var key = Format.key; + if (key && !formatMap[key]) { + formatMap[key] = new Format(); + } + } + + formats.forEach(addToMap); + this.formatMap = formatMap; + } + + FormatProvider.prototype.getFormat = function (key) { + var format = this.formatMap[key]; + if (!format) { + throw new Error("FormatProvider: No format found for " + key); + } + return format; + }; + + return FormatProvider; + +}); diff --git a/platform/commonUI/formats/src/UTCTimeFormat.js b/platform/commonUI/formats/src/UTCTimeFormat.js new file mode 100644 index 0000000000..b035fed99f --- /dev/null +++ b/platform/commonUI/formats/src/UTCTimeFormat.js @@ -0,0 +1,63 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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 define*/ + +define([ + 'moment' +], function ( + moment +) { + "use strict"; + + var DATE_FORMAT = "YYYY-MM-DD HH:mm:ss", + DATE_FORMATS = [ + DATE_FORMAT, + "YYYY-MM-DD HH:mm", + "YYYY-MM-DD" + ]; + + + /** + * Formatter for UTC timestamps. Interprets numeric values as + * milliseconds since the start of 1970. + * + * @implements {Format} + * @constructor + * @memberof platform/commonUI/formats + */ + function UTCTimeFormat() { + } + + UTCTimeFormat.prototype.format = function (value) { + return moment.utc(value).format(DATE_FORMAT); + }; + + UTCTimeFormat.prototype.parse = function (text) { + return moment.utc(text, DATE_FORMATS).valueOf(); + }; + + UTCTimeFormat.prototype.validate = function (text) { + return moment.utc(text, DATE_FORMATS).isValid(); + }; + + return UTCTimeFormat; +}); diff --git a/platform/commonUI/formats/test/FormatProviderSpec.js b/platform/commonUI/formats/test/FormatProviderSpec.js new file mode 100644 index 0000000000..4f68c106f7 --- /dev/null +++ b/platform/commonUI/formats/test/FormatProviderSpec.js @@ -0,0 +1,68 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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 define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/ + +define( + ['../src/FormatProvider'], + function (FormatProvider) { + 'use strict'; + + var KEYS = [ 'a', 'b', 'c' ]; + + describe("The FormatProvider", function () { + var mockFormats, + mockLog, + mockFormatInstances, + provider; + + beforeEach(function () { + mockFormatInstances = KEYS.map(function (k) { + return jasmine.createSpyObj( + 'format-' + k, + [ 'parse', 'validate', 'format' ] + ); + }); + // Return constructors + mockFormats = KEYS.map(function (k, i) { + function MockFormat() { return mockFormatInstances[i]; } + MockFormat.key = k; + return MockFormat; + }); + provider = new FormatProvider(mockFormats); + }); + + it("looks up formats by key", function () { + KEYS.forEach(function (k, i) { + expect(provider.getFormat(k)) + .toEqual(mockFormatInstances[i]); + }); + }); + + it("throws an error about unknown formats", function () { + expect(function () { + provider.getFormat('some-unknown-format'); + }).toThrow(); + }); + + }); + } +); diff --git a/platform/commonUI/formats/test/UTCTimeFormatSpec.js b/platform/commonUI/formats/test/UTCTimeFormatSpec.js new file mode 100644 index 0000000000..d55a8a9507 --- /dev/null +++ b/platform/commonUI/formats/test/UTCTimeFormatSpec.js @@ -0,0 +1,56 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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 define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/ + +define( + ['../src/UTCTimeFormat', 'moment'], + function (UTCTimeFormat, moment) { + 'use strict'; + + describe("The UTCTimeFormat", function () { + var format; + + beforeEach(function () { + format = new UTCTimeFormat(); + }); + + it("formats UTC timestamps", function () { + var timestamp = 12345670000, + formatted = format.format(timestamp); + expect(formatted).toEqual(jasmine.any(String)); + expect(moment.utc(formatted).valueOf()).toEqual(timestamp); + }); + + it("validates time inputs", function () { + expect(format.validate("1977-05-25 11:21:22")).toBe(true); + expect(format.validate("garbage text")).toBe(false); + }); + + it("parses valid input", function () { + var text = "1977-05-25 11:21:22", + parsed = format.parse(text); + expect(parsed).toEqual(jasmine.any(Number)); + expect(parsed).toEqual(moment.utc(text).valueOf()); + }); + }); + } +); diff --git a/platform/commonUI/formats/test/suite.json b/platform/commonUI/formats/test/suite.json new file mode 100644 index 0000000000..06c88fac8b --- /dev/null +++ b/platform/commonUI/formats/test/suite.json @@ -0,0 +1,4 @@ +[ + "FormatProvider", + "UTCTimeFormat" +] diff --git a/platform/commonUI/general/bundle.json b/platform/commonUI/general/bundle.json index 1aa0b1dfc1..edaa8ec103 100644 --- a/platform/commonUI/general/bundle.json +++ b/platform/commonUI/general/bundle.json @@ -44,6 +44,14 @@ "key": "indicator", "templateUrl": "templates/indicator.html" }, + { + "key": "message-banner", + "templateUrl": "templates/message-banner.html" + }, + { + "key": "progress-bar", + "templateUrl": "templates/progress-bar.html" + }, { "key": "time-controller", "templateUrl": "templates/controls/time-controller.html" @@ -53,13 +61,18 @@ { "key": "TimeRangeController", "implementation": "controllers/TimeRangeController.js", - "depends": [ "$scope", "now" ] + "depends": [ "$scope", "formatService", "DEFAULT_TIME_FORMAT", "now" ] }, { "key": "DateTimePickerController", "implementation": "controllers/DateTimePickerController.js", "depends": [ "$scope", "now" ] }, + { + "key": "DateTimeFieldController", + "implementation": "controllers/DateTimeFieldController.js", + "depends": [ "$scope", "formatService", "DEFAULT_TIME_FORMAT" ] + }, { "key": "TreeNodeController", "implementation": "controllers/TreeNodeController.js", @@ -107,6 +120,16 @@ "key": "SelectorController", "implementation": "controllers/SelectorController.js", "depends": [ "objectService", "$scope" ] + }, + { + "key": "ObjectInspectorController", + "implementation": "controllers/ObjectInspectorController.js", + "depends": [ "$scope", "objectService" ] + }, + { + "key": "BannerController", + "implementation": "controllers/BannerController.js", + "depends": ["$scope", "notificationService", "dialogService"] } ], "directives": [ @@ -232,6 +255,10 @@ "key": "switcher", "templateUrl": "templates/controls/switcher.html", "uses": [ "view" ] + }, + { + "key": "object-inspector", + "templateUrl": "templates/object-inspector.html" } ], "controls": [ @@ -242,6 +269,10 @@ { "key": "datetime-picker", "templateUrl": "templates/controls/datetime-picker.html" + }, + { + "key": "datetime-field", + "templateUrl": "templates/controls/datetime-field.html" } ], "licenses": [ diff --git a/platform/commonUI/general/res/fonts/symbols/icomoon.io-WTD-symbols-project.json b/platform/commonUI/general/res/fonts/symbols/icomoon.io-WTD-symbols-project.json index 7982d57200..489d491fd2 100644 --- a/platform/commonUI/general/res/fonts/symbols/icomoon.io-WTD-symbols-project.json +++ b/platform/commonUI/general/res/fonts/symbols/icomoon.io-WTD-symbols-project.json @@ -1,14 +1,70 @@ { "metadata": { - "name": "WTD Symbols v24", - "lastOpened": 1441992412958, - "created": 1441992410384 + "name": "WTD Symbols", + "lastOpened": 1446490786311, + "created": 1446489891263 }, "iconSets": [ { "selection": [ { - "order": 86, + "order": 113, + "id": 92, + "prevSize": 32, + "code": 58901, + "name": "icon-crosshair", + "tempChar": "" + }, + { + "order": 110, + "id": 91, + "prevSize": 32, + "code": 58899, + "name": "icon-collapse-pane-left", + "tempChar": "" + }, + { + "order": 111, + "id": 90, + "prevSize": 32, + "code": 58900, + "name": "icon-collapse-pane-right", + "tempChar": "" + }, + { + "order": 109, + "id": 89, + "prevSize": 32, + "code": 58898, + "name": "icon-save", + "tempChar": "" + }, + { + "order": 108, + "id": 88, + "prevSize": 32, + "code": 58897, + "name": "icon-dataset", + "tempChar": "" + }, + { + "order": 90, + "id": 87, + "prevSize": 32, + "code": 58896, + "name": "icon-bell", + "tempChar": "" + }, + { + "order": 91, + "id": 86, + "prevSize": 32, + "code": 58889, + "name": "icon-hourglass", + "tempChar": "" + }, + { + "order": 92, "id": 85, "prevSize": 32, "code": 58888, @@ -18,119 +74,119 @@ 58890 ], "name": "icon-info-v15", - "tempChar": "" + "tempChar": "" }, { - "order": 82, + "order": 93, "id": 84, "prevSize": 32, "code": 58887, "name": "icon-x-in-circle", - "tempChar": "" + "tempChar": "" }, { - "order": 77, + "order": 94, "id": 83, "prevSize": 32, "code": 58881, "name": "icon-datatable", - "tempChar": "" + "tempChar": "" }, { - "order": 78, + "order": 95, "id": 82, "prevSize": 32, "code": 58882, "name": "icon-tabular-scrolling", - "tempChar": "" + "tempChar": "" }, { - "order": 79, + "order": 96, "id": 81, "prevSize": 32, "code": 58884, "name": "icon-tabular", - "tempChar": "" + "tempChar": "" }, { - "order": 80, + "order": 97, "id": 80, "prevSize": 32, "code": 58885, "name": "icon-calendar", - "tempChar": "" + "tempChar": "" }, { - "order": 83, + "order": 98, "id": 78, "prevSize": 32, "code": 58886, "name": "icon-paint-bucket", - "tempChar": "" + "tempChar": "" }, { - "order": 1, + "order": 99, "id": 75, "prevSize": 32, "code": 123, "name": "icon-pointer-left", - "tempChar": "" + "tempChar": "" }, { - "order": 3, + "order": 100, "id": 74, "prevSize": 32, "code": 125, "name": "icon-pointer-right", - "tempChar": "" + "tempChar": "" }, { - "order": 4, + "order": 101, "id": 73, "prevSize": 32, "code": 80, "name": "icon-person", - "tempChar": "" + "tempChar": "" }, { - "order": 5, + "order": 102, "id": 72, "prevSize": 32, "code": 232, "name": "icon-chain-links", - "tempChar": "" + "tempChar": "" }, { - "order": 6, + "order": 103, "id": 71, "prevSize": 32, "code": 115, "name": "icon-database-in-brackets", - "tempChar": "" + "tempChar": "" }, { - "order": 7, + "order": 104, "id": 70, "prevSize": 32, "code": 114, "name": "icon-refresh", - "tempChar": "" + "tempChar": "" }, { - "order": 8, + "order": 105, "id": 69, "prevSize": 32, "code": 108, "name": "icon-lock", - "tempChar": "" + "tempChar": "" }, { - "order": 9, + "order": 106, "id": 68, "prevSize": 32, "code": 51, "name": "icon-box-with-dashed-lines", - "tempChar": "" + "tempChar": "" }, { "order": 10, @@ -138,7 +194,7 @@ "prevSize": 32, "code": 58880, "name": "icon-box-with-arrow-cursor", - "tempChar": "" + "tempChar": "" }, { "order": 11, @@ -146,7 +202,7 @@ "prevSize": 32, "code": 65, "name": "icon-activity-mode", - "tempChar": "" + "tempChar": "" }, { "order": 12, @@ -154,15 +210,15 @@ "prevSize": 32, "code": 97, "name": "icon-activity", - "tempChar": "" + "tempChar": "" }, { - "order": 13, + "order": 87, "id": 64, "prevSize": 32, "code": 33, "name": "icon-alert-rect", - "tempChar": "" + "tempChar": "" }, { "order": 14, @@ -170,7 +226,7 @@ "prevSize": 32, "code": 58883, "name": "icon-alert-triangle", - "tempChar": "" + "tempChar": "" }, { "order": 15, @@ -178,7 +234,7 @@ "prevSize": 32, "code": 238, "name": "icon-arrow-double-down", - "tempChar": "" + "tempChar": "" }, { "order": 16, @@ -186,7 +242,7 @@ "prevSize": 32, "code": 235, "name": "icon-arrow-double-up", - "tempChar": "" + "tempChar": "" }, { "order": 2, @@ -194,7 +250,7 @@ "prevSize": 32, "code": 118, "name": "icon-arrow-down", - "tempChar": "" + "tempChar": "" }, { "order": 19, @@ -202,7 +258,7 @@ "prevSize": 32, "code": 60, "name": "icon-arrow-left", - "tempChar": "" + "tempChar": "" }, { "order": 20, @@ -210,7 +266,7 @@ "prevSize": 32, "code": 62, "name": "icon-arrow-right", - "tempChar": "" + "tempChar": "" }, { "order": 21, @@ -218,7 +274,7 @@ "prevSize": 32, "code": 236, "name": "icon-arrow-tall-down", - "tempChar": "" + "tempChar": "" }, { "order": 22, @@ -226,7 +282,7 @@ "prevSize": 32, "code": 237, "name": "icon-arrow-tall-up", - "tempChar": "" + "tempChar": "" }, { "order": 23, @@ -234,7 +290,7 @@ "prevSize": 32, "code": 94, "name": "icon-arrow-up", - "tempChar": "" + "tempChar": "" }, { "order": 24, @@ -242,7 +298,7 @@ "prevSize": 32, "code": 73, "name": "icon-arrows-out", - "tempChar": "" + "tempChar": "" }, { "order": 25, @@ -250,7 +306,7 @@ "prevSize": 32, "code": 58893, "name": "icon-arrows-right-left", - "tempChar": "" + "tempChar": "" }, { "order": 33, @@ -258,7 +314,7 @@ "prevSize": 32, "code": 53, "name": "icon-arrows-up-down", - "tempChar": "" + "tempChar": "" }, { "order": 26, @@ -266,7 +322,7 @@ "prevSize": 32, "code": 42, "name": "icon-asterisk", - "tempChar": "" + "tempChar": "" }, { "order": 27, @@ -274,7 +330,7 @@ "prevSize": 32, "code": 72, "name": "icon-autoflow-tabular", - "tempChar": "" + "tempChar": "" }, { "order": 28, @@ -282,7 +338,7 @@ "prevSize": 32, "code": 224, "name": "icon-box", - "tempChar": "" + "tempChar": "" }, { "order": 29, @@ -290,7 +346,7 @@ "prevSize": 32, "code": 50, "name": "icon-check", - "tempChar": "" + "tempChar": "" }, { "order": 30, @@ -298,7 +354,7 @@ "prevSize": 32, "code": 67, "name": "icon-clock", - "tempChar": "" + "tempChar": "" }, { "order": 31, @@ -306,7 +362,7 @@ "prevSize": 32, "code": 46, "name": "icon-connectivity", - "tempChar": "" + "tempChar": "" }, { "order": 32, @@ -314,7 +370,7 @@ "prevSize": 32, "code": 100, "name": "icon-database-query", - "tempChar": "" + "tempChar": "" }, { "order": 17, @@ -322,7 +378,7 @@ "prevSize": 32, "code": 68, "name": "icon-database", - "tempChar": "" + "tempChar": "" }, { "order": 35, @@ -330,7 +386,7 @@ "prevSize": 32, "code": 81, "name": "icon-dictionary", - "tempChar": "" + "tempChar": "" }, { "order": 36, @@ -338,7 +394,7 @@ "prevSize": 32, "code": 242, "name": "icon-duplicate", - "tempChar": "" + "tempChar": "" }, { "order": 37, @@ -346,7 +402,7 @@ "prevSize": 32, "code": 102, "name": "icon-folder-new", - "tempChar": "" + "tempChar": "" }, { "order": 38, @@ -354,7 +410,7 @@ "prevSize": 32, "code": 70, "name": "icon-folder", - "tempChar": "" + "tempChar": "" }, { "order": 39, @@ -362,7 +418,7 @@ "prevSize": 32, "code": 95, "name": "icon-fullscreen-collapse", - "tempChar": "" + "tempChar": "" }, { "order": 40, @@ -370,7 +426,7 @@ "prevSize": 32, "code": 122, "name": "icon-fullscreen-expand", - "tempChar": "" + "tempChar": "" }, { "order": 41, @@ -378,7 +434,7 @@ "prevSize": 32, "code": 71, "name": "icon-gear", - "tempChar": "" + "tempChar": "" }, { "order": 49, @@ -386,7 +442,7 @@ "prevSize": 32, "code": 227, "name": "icon-image", - "tempChar": "" + "tempChar": "" }, { "order": 42, @@ -394,7 +450,7 @@ "prevSize": 32, "code": 225, "name": "icon-layers", - "tempChar": "" + "tempChar": "" }, { "order": 43, @@ -402,7 +458,7 @@ "prevSize": 32, "code": 76, "name": "icon-layout", - "tempChar": "" + "tempChar": "" }, { "order": 44, @@ -410,7 +466,7 @@ "prevSize": 32, "code": 226, "name": "icon-line-horz", - "tempChar": "" + "tempChar": "" }, { "order": 75, @@ -418,7 +474,7 @@ "prevSize": 32, "code": 244, "name": "icon-link", - "tempChar": "" + "tempChar": "" }, { "order": 46, @@ -426,7 +482,7 @@ "prevSize": 32, "code": 88, "name": "icon-magnify-in", - "tempChar": "" + "tempChar": "" }, { "order": 47, @@ -434,7 +490,7 @@ "prevSize": 32, "code": 89, "name": "icon-magnify-out", - "tempChar": "" + "tempChar": "" }, { "order": 48, @@ -442,7 +498,7 @@ "prevSize": 32, "code": 77, "name": "icon-magnify", - "tempChar": "" + "tempChar": "" }, { "order": 34, @@ -450,7 +506,7 @@ "prevSize": 32, "code": 109, "name": "icon-menu", - "tempChar": "" + "tempChar": "" }, { "order": 50, @@ -458,7 +514,7 @@ "prevSize": 32, "code": 243, "name": "icon-move", - "tempChar": "" + "tempChar": "" }, { "order": 51, @@ -466,7 +522,7 @@ "prevSize": 32, "code": 121, "name": "icon-new-window", - "tempChar": "" + "tempChar": "" }, { "order": 52, @@ -474,7 +530,7 @@ "prevSize": 32, "code": 111, "name": "icon-object", - "tempChar": "" + "tempChar": "" }, { "order": 73, @@ -482,7 +538,7 @@ "prevSize": 32, "code": 63, "name": "icon-object-unknown", - "tempChar": "" + "tempChar": "" }, { "order": 53, @@ -490,7 +546,7 @@ "prevSize": 32, "code": 86, "name": "icon-packet", - "tempChar": "" + "tempChar": "" }, { "order": 54, @@ -498,7 +554,7 @@ "prevSize": 32, "code": 234, "name": "icon-page", - "tempChar": "" + "tempChar": "" }, { "order": 55, @@ -506,7 +562,7 @@ "prevSize": 32, "code": 241, "name": "icon-pause", - "tempChar": "" + "tempChar": "" }, { "order": 56, @@ -514,7 +570,7 @@ "prevSize": 32, "code": 112, "name": "icon-pencil", - "tempChar": "" + "tempChar": "" }, { "order": 65, @@ -522,7 +578,7 @@ "prevSize": 32, "code": 79, "name": "icon-people", - "tempChar": "" + "tempChar": "" }, { "order": 57, @@ -530,7 +586,7 @@ "prevSize": 32, "code": 239, "name": "icon-play", - "tempChar": "" + "tempChar": "" }, { "order": 58, @@ -538,7 +594,7 @@ "prevSize": 32, "code": 233, "name": "icon-plot-resource", - "tempChar": "" + "tempChar": "" }, { "order": 59, @@ -546,7 +602,7 @@ "prevSize": 32, "code": 43, "name": "icon-plus", - "tempChar": "" + "tempChar": "" }, { "order": 60, @@ -554,7 +610,7 @@ "prevSize": 32, "code": 45, "name": "icon-minus", - "tempChar": "" + "tempChar": "" }, { "order": 61, @@ -562,7 +618,7 @@ "prevSize": 32, "code": 54, "name": "icon-sine", - "tempChar": "" + "tempChar": "" }, { "order": 62, @@ -570,7 +626,7 @@ "prevSize": 32, "code": 228, "name": "icon-T", - "tempChar": "" + "tempChar": "" }, { "order": 63, @@ -578,7 +634,7 @@ "prevSize": 32, "code": 116, "name": "icon-telemetry-panel", - "tempChar": "" + "tempChar": "" }, { "order": 64, @@ -586,7 +642,7 @@ "prevSize": 32, "code": 84, "name": "icon-telemetry", - "tempChar": "" + "tempChar": "" }, { "order": 18, @@ -594,7 +650,7 @@ "prevSize": 32, "code": 246, "name": "icon-thumbs-strip", - "tempChar": "" + "tempChar": "" }, { "order": 67, @@ -602,7 +658,7 @@ "prevSize": 32, "code": 83, "name": "icon-timeline", - "tempChar": "" + "tempChar": "" }, { "order": 68, @@ -610,7 +666,7 @@ "prevSize": 32, "code": 245, "name": "icon-timer", - "tempChar": "" + "tempChar": "" }, { "order": 69, @@ -618,7 +674,7 @@ "prevSize": 32, "code": 90, "name": "icon-trash", - "tempChar": "" + "tempChar": "" }, { "order": 70, @@ -626,7 +682,7 @@ "prevSize": 32, "code": 229, "name": "icon-two-parts-both", - "tempChar": "" + "tempChar": "" }, { "order": 71, @@ -634,7 +690,7 @@ "prevSize": 32, "code": 231, "name": "icon-two-parts-one-only", - "tempChar": "" + "tempChar": "" }, { "order": 72, @@ -642,7 +698,7 @@ "prevSize": 32, "code": 120, "name": "icon-x-heavy", - "tempChar": "" + "tempChar": "" }, { "order": 66, @@ -650,7 +706,7 @@ "prevSize": 32, "code": 58946, "name": "icon-x", - "tempChar": "" + "tempChar": "" } ], "id": 2, @@ -665,6 +721,173 @@ "height": 1024, "prevSize": 32, "icons": [ + { + "id": 92, + "paths": [ + "M514 2c-282.8 0-512 229.2-512 512s229.2 512 512 512 512-229.2 512-512-229.2-512-512-512zM860.2 450h-282.2v-282.2c69.6 12.8 133.8 46.2 185 97.4 51 51 84.4 115.2 97.2 184.8zM450 167.8v282.2h-282.2c12.8-69.6 46.2-133.8 97.4-185 51-51 115.2-84.4 184.8-97.2zM167.8 578h282.2v282.2c-69.6-12.8-133.8-46.2-185-97.4-51-51-84.4-115.2-97.2-184.8zM578 860.2v-282.2h282.2c-12.8 69.6-46.2 133.8-97.4 185-51 51-115.2 84.4-184.8 97.2z" + ], + "attrs": [ + { + "fill": "rgb(0, 0, 0)" + } + ], + "isMulticolor": false, + "grid": 0, + "tags": [ + "icon-crosshair" + ], + "colorPermutations": { + "125525525516161751": [ + 0 + ] + } + }, + { + "id": 91, + "paths": [ + "M256 0h-256v1024h256c105.6 0 192-86.4 192-192v-640c0-105.6-86.4-192-192-192z", + "M512 320l512 320v-640z" + ], + "attrs": [ + { + "fill": "rgb(0, 0, 0)" + }, + { + "fill": "rgb(0, 0, 0)" + } + ], + "isMulticolor": false, + "grid": 0, + "tags": [ + "icon-collapse-pane-left" + ], + "colorPermutations": { + "125525525516161751": [ + 0, + 0 + ] + } + }, + { + "id": 90, + "paths": [ + "M768 0h256v1024h-256c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192z", + "M512 320l-512 320v-640z" + ], + "attrs": [ + { + "fill": "rgb(0, 0, 0)" + }, + { + "fill": "rgb(0, 0, 0)" + } + ], + "isMulticolor": false, + "grid": 0, + "tags": [ + "icon-collapse-pane-right" + ], + "colorPermutations": { + "125525525516161751": [ + 0, + 0 + ] + } + }, + { + "id": 89, + "paths": [ + "M192.2 576c-0.2 0-0.2 0 0 0l-0.2 448h640v-447.8c0 0 0 0-0.2-0.2h-639.6z", + "M978.8 210.8l-165.4-165.4c-25-25-74.2-45.4-109.4-45.4h-576c-70.4 0-128 57.6-128 128v768c0 70.4 57.6 128 128 128v-448c0-35.2 28.8-64 64-64h640c35.2 0 64 28.8 64 64v448c70.4 0 128-57.6 128-128v-576c0-35.2-20.4-84.4-45.2-109.2zM704 256c0 35.2-28.8 64-64 64h-448c-35.2 0-64-28.8-64-64v-192h320v192h128v-192h128v192z" + ], + "attrs": [ + { + "fill": "rgb(0, 0, 0)" + }, + { + "fill": "rgb(0, 0, 0)" + } + ], + "isMulticolor": false, + "grid": 0, + "tags": [ + "icon-save-v2" + ], + "colorPermutations": { + "125525525516161751": [ + 0, + 0 + ] + } + }, + { + "id": 88, + "paths": [ + "M896 192h-320c-16.4-16.4-96.8-96.8-109.2-109.2l-37.4-37.4c-25-25-74.2-45.4-109.4-45.4h-256c-35.2 0-64 28.8-64 64v384c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v-128c0-70.4-57.6-128-128-128z", + "M896 448h-768c-70.4 0-128 57.6-128 128v320c0 70.4 57.6 128 128 128h768c70.4 0 128-57.6 128-128v-320c0-70.4-57.6-128-128-128zM320 896h-128v-320h128v320zM576 896h-128v-320h128v320zM832 896h-128v-320h128v320z" + ], + "attrs": [], + "isMulticolor": false, + "grid": 0, + "tags": [ + "icon-dataset" + ], + "colorPermutations": { + "125525525516161751": [] + } + }, + { + "id": 87, + "paths": [ + "M512 1024c106 0 192-86 192-192h-384c0 106 86 192 192 192z", + "M896 448v-64c0-212-172-384-384-384s-384 172-384 384v64c0 70.6-57.4 128-128 128v128h1024v-128c-70.6 0-128-57.4-128-128z" + ], + "attrs": [ + { + "fill": "rgb(6, 161, 75)" + }, + { + "fill": "rgb(6, 161, 75)" + } + ], + "isMulticolor": false, + "grid": 0, + "tags": [ + "icon-bell" + ], + "colorPermutations": { + "125525525516161751": [ + 1, + 1 + ] + } + }, + { + "id": 86, + "paths": [ + "M1024 0h-1024c0 282.8 229.2 512 512 512s512-229.2 512-512zM512 384c-102.6 0-199-40-271.6-112.4-41.2-41.2-72-90.2-90.8-143.6h724.6c-18.8 53.4-49.6 102.4-90.8 143.6-72.4 72.4-168.8 112.4-271.4 112.4z", + "M512 512c-282.8 0-512 229.2-512 512h1024c0-282.8-229.2-512-512-512z" + ], + "attrs": [ + { + "fill": "rgb(6, 161, 75)" + }, + { + "fill": "rgb(6, 161, 75)" + } + ], + "isMulticolor": false, + "grid": 0, + "tags": [ + "icon-hourglass" + ], + "colorPermutations": { + "125525525516161751": [ + 1, + 1 + ] + } + }, { "id": 85, "paths": [ @@ -698,7 +921,8 @@ "icon-x-in-circle" ], "colorPermutations": { - "16161751": [] + "16161751": [], + "125525525516161751": [] } }, { @@ -899,6 +1123,11 @@ 1, 1, 1 + ], + "125525525516161751": [ + 1, + 1, + 1 ] } }, @@ -1051,18 +1280,28 @@ { "id": 67, "paths": [ - "M832 512.4c0-0.2 0-0.2 0-0.4v-320c0-105.6-86.4-192-192-192h-448c-105.6 0-192 86.4-192 192v320c0 105.6 86.4 192 192 192h263.6l-197.2-445.6 573.6 254z", - "M766.8 659.8l193.8-20.4-576.6-255.4 255.4 576.6 20.4-193.8 257 257.2 107.2-107.2z" + "M894-2h-768c-70.4 0-128 57.6-128 128v768c0 70.4 57.6 128 128 128h400c-2.2-3.8-4-7.6-5.8-11.4l-255.2-576.8c-21.4-48.4-10.8-105 26.6-142.4 24.4-24.4 57.2-37.4 90.4-37.4 17.4 0 35.2 3.6 51.8 11l576.6 255.4c4 1.8 7.8 3.8 11.4 5.8v-400.2c0.2-70.4-57.4-128-127.8-128z", + "M958.6 637.4l-576.6-255.4 255.4 576.6 64.6-128.6 192 192 128-128-192-192z" ], "attrs": [ - {}, - {} + { + "fill": "rgb(0, 0, 0)" + }, + { + "fill": "rgb(0, 0, 0)" + } ], "isMulticolor": false, "grid": 0, "tags": [ "icon-box-with-arrow-cursor" - ] + ], + "colorPermutations": { + "125525525516161751": [ + 0, + 0 + ] + } }, { "id": 66, @@ -1338,6 +1577,9 @@ "colorPermutations": { "16161751": [ 0 + ], + "125525525516161751": [ + 0 ] } }, @@ -14853,6 +15095,5 @@ "gridSize": 16, "showLiga": false }, - "uid": -1, - "time": 1441993324496 + "uid": -1 } \ No newline at end of file diff --git a/platform/commonUI/general/res/fonts/symbols/wtdsymbols.eot b/platform/commonUI/general/res/fonts/symbols/wtdsymbols.eot index 95887455fe..43192b87f7 100755 Binary files a/platform/commonUI/general/res/fonts/symbols/wtdsymbols.eot and b/platform/commonUI/general/res/fonts/symbols/wtdsymbols.eot differ diff --git a/platform/commonUI/general/res/fonts/symbols/wtdsymbols.svg b/platform/commonUI/general/res/fonts/symbols/wtdsymbols.svg index 596a5dd3e9..455f2db341 100755 --- a/platform/commonUI/general/res/fonts/symbols/wtdsymbols.svg +++ b/platform/commonUI/general/res/fonts/symbols/wtdsymbols.svg @@ -76,7 +76,7 @@ - + @@ -85,6 +85,13 @@ + + + + + + + \ No newline at end of file diff --git a/platform/commonUI/general/res/fonts/symbols/wtdsymbols.ttf b/platform/commonUI/general/res/fonts/symbols/wtdsymbols.ttf index 8f0f1d9610..0bb5126e8a 100755 Binary files a/platform/commonUI/general/res/fonts/symbols/wtdsymbols.ttf and b/platform/commonUI/general/res/fonts/symbols/wtdsymbols.ttf differ diff --git a/platform/commonUI/general/res/fonts/symbols/wtdsymbols.woff b/platform/commonUI/general/res/fonts/symbols/wtdsymbols.woff index d5bc74071c..0b1935282d 100755 Binary files a/platform/commonUI/general/res/fonts/symbols/wtdsymbols.woff and b/platform/commonUI/general/res/fonts/symbols/wtdsymbols.woff differ diff --git a/platform/commonUI/general/res/sass/_constants.scss b/platform/commonUI/general/res/sass/_constants.scss index 6ef636cdcd..a3305cf0a4 100644 --- a/platform/commonUI/general/res/sass/_constants.scss +++ b/platform/commonUI/general/res/sass/_constants.scss @@ -42,13 +42,17 @@ $ueFooterH: 25px; $ueColMargin: 1.5%; $ueAppLogoW: 105px; $ueEditToolBarH: 25px; -$ueBrowseLeftPaneW: 25%; +$ueBrowseLeftPaneTreeW: 25%; +$ueBrowseRightPaneInspectW: 20%; +$ueCollapsedPaneEdgeM: 20px; +$uePaneMiniTabH: $ueTopBarH; +$uePaneMiniTabW: 9px; $ueEditLeftPaneW: 75%; $treeSearchInputBarH: 25px; $ueTimeControlH: (33px, 20px, 20px); // Overlay -$ovrTopBarH: 60px; -$ovrFooterH: 30px; +$ovrTopBarH: 45px; +$ovrFooterH: 24px; $overlayMargin: 25px; // Items $ueBrowseGridItemLg: 200px; @@ -57,7 +61,8 @@ $ueBrowseGridItemBottomBarH: 30px; $itemPadLR: 5px; // Tree $treeVCW: 10px; -$treeTypeIconH: 16px; +$treeTypeIconH: 1.4em; // was 16px +$treeTypeIconHPx: 16px; $treeTypeIconW: 20px; $treeContextTriggerW: 20px; // Tabular diff --git a/platform/commonUI/general/res/sass/_global.scss b/platform/commonUI/general/res/sass/_global.scss index 45b868922b..5b2011f5bb 100644 --- a/platform/commonUI/general/res/sass/_global.scss +++ b/platform/commonUI/general/res/sass/_global.scss @@ -34,10 +34,6 @@ font-style: normal; } -.ui-symbol { - font-family: 'symbolsfont'; -} - /************************** HTML ENTITIES */ a { color: $colorA; @@ -55,7 +51,7 @@ body, html { color: $colorBodyFg; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 100%; - //font-weight: 500; + font-weight: 200; height: 100%; width: 100%; overflow: hidden; diff --git a/platform/commonUI/general/res/sass/_icons.scss b/platform/commonUI/general/res/sass/_icons.scss index 1208b3e7cf..3b41b31011 100644 --- a/platform/commonUI/general/res/sass/_icons.scss +++ b/platform/commonUI/general/res/sass/_icons.scss @@ -29,11 +29,14 @@ } .ui-symbol { + font-family: 'symbolsfont'; &.type-icon { color: $colorObjHdrIc; } &.icon { color: $colorKey; + //position: relative; + font-size: inherit; &.alert { color: $colorAlert; &:hover { @@ -69,18 +72,32 @@ position: absolute; } -//.tree-item .type-icon { -// font-size: 16px; // 16px is crisp size -//} - -.l-icon-link:before { - content: "\f4"; -} - .l-icon-alert { display: none !important; // Remove this when alerts are enabled &:before { color: $colorAlert; content: "!"; } +} + +// NEW!! +.t-item-icon { + // Used in grid-item.html, tree-item, inspector location, more? + @extend .ui-symbol; + @extend .icon; + display: inline-block; + line-height: normal; // This is Ok for the symbolsfont + position: relative; + &.l-icon-link { + &:before { + color: $colorIconLink; + content: "\f4"; + height: auto; width: auto; + position: absolute; + left: 0; top: 0; right: 0; bottom: 10%; + @include transform-origin(bottom, left); + @include transform(scale(0.3)); + z-index: 2; + } + } } \ No newline at end of file diff --git a/platform/commonUI/general/res/sass/_inspector.scss b/platform/commonUI/general/res/sass/_inspector.scss new file mode 100644 index 0000000000..9f67ff3b73 --- /dev/null +++ b/platform/commonUI/general/res/sass/_inspector.scss @@ -0,0 +1,107 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ +/* Styles for the Inspector pane */ + +.l-inspect, +.l-inspect table tr td { + font-size: 0.7rem; +} + +.l-inspect { + @extend .abs; + background: $colorInspectorBg; + color: $colorInspectorFg; + line-height: 140%; + .pane-header { + color: pushBack($colorInspectorFg, 20%); + font-size: 0.8rem; + &:before { + color: pushBack($colorInspectorFg, 10%); + content:'\e615'; // e615 Crosshair symbol + display: inline; + font-family: symbolsfont; + margin-right: $interiorMargin; + vertical-align: bottom; + } + } + + ul li, + em { + display: block; + position: relative; + } + + ul li { + margin-bottom: $interiorMarginLg; + } + + em { + @include border-radius($basicCr); + background-color: $colorInspectorSectionHeaderBg; + color: $colorInspectorSectionHeaderFg; + margin-bottom: $interiorMargin; + padding: $formTBPad $formLRPad; + text-transform: uppercase; + } + + .inspector-properties { + &:not(.first) { + border-top: 1px solid $colorInspectorSectionHeaderBg; + } + padding: $interiorMarginSm 0; + .label { + color: $colorInspectorPropName; + text-transform: uppercase; + } + .value { + color: $colorInspectorPropVal; + word-break: break-all; + } + } + + .inspector-location { + //line-height: 180%; + .location-item { + cursor: pointer; + display: inline-block; + position: relative; + padding: 2px 4px; + &:hover { + background: $colorItemTreeHoverBg; + color: $colorItemTreeHoverFg; + .icon { + color: $colorItemTreeIconHover; + } + } + } + &:not(.last) .t-object-label .t-title-label:after { + color: pushBack($colorInspectorFg, 15%); + content: '\3e'; + display: inline-block; + font-family: symbolsfont; + font-size: 8px; + line-height: inherit; + margin-left: $interiorMarginSm; + width: 4px; + } + } +} \ No newline at end of file diff --git a/platform/commonUI/general/res/sass/_main.scss b/platform/commonUI/general/res/sass/_main.scss index 80023f55ac..0dab445e84 100644 --- a/platform/commonUI/general/res/sass/_main.scss +++ b/platform/commonUI/general/res/sass/_main.scss @@ -29,8 +29,7 @@ @import "helpers/bubbles"; @import "helpers/splitter"; @import "helpers/wait-spinner"; -@import "messages"; -@import "properties"; +@import "inspector"; /********************************* CONTROLS */ @import "controls/breadcrumb"; @@ -39,6 +38,7 @@ @import "controls/controls"; @import "controls/lists"; @import "controls/menus"; +@import "controls/messages"; @import "controls/time-controller"; @import "mobile/controls/menus"; @@ -62,7 +62,6 @@ @import "mobile/tree"; @import "user-environ/frame"; @import "user-environ/top-bar"; -@import "user-environ/bottom-bar"; @import "user-environ/tool-bar"; /********************************* VIEWS */ @@ -70,7 +69,6 @@ @import "lists/tabular"; @import "plots/plots-main"; @import "iframe"; -@import "hide-non-functional"; @import "views"; @import "items/item"; @import "mobile/item"; diff --git a/platform/commonUI/general/res/sass/_messages.scss b/platform/commonUI/general/res/sass/_messages.scss deleted file mode 100644 index db4de4c946..0000000000 --- a/platform/commonUI/general/res/sass/_messages.scss +++ /dev/null @@ -1,12 +0,0 @@ -/* Styles for messages */ - -.message { - &.block { - @include border-radius($basicCr); - padding: $interiorMarginLg; - } - &.error { - background-color: rgba($colorAlert,0.3); - color: lighten($colorAlert, 20%); - } -} \ No newline at end of file diff --git a/platform/commonUI/general/res/sass/_mixins.scss b/platform/commonUI/general/res/sass/_mixins.scss index c3d2ecc781..f5a21cf9dd 100644 --- a/platform/commonUI/general/res/sass/_mixins.scss +++ b/platform/commonUI/general/res/sass/_mixins.scss @@ -41,36 +41,41 @@ width: $d; } -@mixin trans-prop-nice($props, $t: 500ms) { - @if $t == 0 { +@mixin trans-prop-nice($props, $dur: 500ms, $delay: 0) { + // Multiple $props must be in parans like this: (left, right) + @if $dur == 0 { @include transition-property(none); } @else { @include transition-property($props); - @include transition-duration($t); + @include transition-duration($dur); @include transition-timing-function(ease-in-out); + @include transition-delay($delay); } } -@mixin trans-prop-nice-fade($t: 0.5s) { - @if $t == 0 { +@mixin trans-prop-nice-fade($dur: 500ms, $delay: 0) { + @if $dur == 0 { @include transition-property(none); } @else { - @include transition-property(visibility, opacity, background-color, border-color); - @include transition-duration($t); + @include transition-property(opacity, background-color, border-color, color); + @include transition-duration($dur); @include transition-timing-function(ease-in-out); + @include transition-delay($delay); } } -@mixin trans-prop-nice-resize-h($t: 0.5s) { +@mixin trans-prop-nice-resize-h($dur: 500ms, $delay: 0) { @include transition-property(height, bottom, top); - @include transition-duration($t); + @include transition-duration($dur); @include transition-timing-function(ease-in-out); + @include transition-delay($delay); } -@mixin trans-prop-nice-resize-w($t: 0.5s) { +@mixin trans-prop-nice-resize-w($dur: 500ms, $delay: 0) { @include transition-property(width, left, right); - @include transition-duration($t); + @include transition-duration($dur); @include transition-timing-function(ease-in-out); + @include transition-delay($delay); } @mixin triangle-right($size, $color) { @@ -94,7 +99,6 @@ } @mixin triangle($dir: "left", $size: 5px, $ratio: 1, $color: red) { - //$size: $size*2; width: 0; height: 0; $slopedB: $size/$ratio solid transparent; @@ -129,6 +133,24 @@ background-size: $d $d; } +@mixin bgVertStripes($c: yellow, $a: 0.1, $d: 40px) { + @include background-image(linear-gradient(-90deg, + rgba($c, $a) 0%, rgba($c, $a) 50%, + transparent 50%, transparent 100% + )); + background-repeat: repeat; + background-size: $d $d; +} + +@mixin bgVertFuzzyStripes($c: yellow, $a: 0.1, $d: 40px) { + @include background-image(linear-gradient(-90deg, + rgba($c, $a) 0%, transparent 50%, + transparent 50%, rgba($c, $a) 100% + )); + background-repeat: repeat; + background-size: $d $d; +} + @mixin bgTicks($c: $colorBodyFg, $repeatDir: 'x') { $deg: 90deg; @if ($repeatDir != 'x') { @@ -156,31 +178,28 @@ } @mixin controlGrippy($b, $direction: horizontal, $w: 1px, $style: dotted) { - &:before { - @include trans-prop-nice("border-color", 0.75s); - content: ''; - display: block; - height: auto; - pointer-events: none; - position: absolute; - z-index: 2; + //&:before { + //@include trans-prop-nice("border-color", 25ms); + content: ''; + display: block; + //height: auto; + pointer-events: none; + position: absolute; + z-index: 2; - @if $direction == "horizontal" { - border-top: $w $style darken($b, 15%); - top: 2px; - left: 5px; - right: 5px; + @if $direction == "horizontal" { + border-top: $w $style darken($b, 15%); + top: 2px; + left: 5px; + right: 5px; + height: 1px; - } @else if $direction == "vertical" { - border-left: $w $style darken($b, 15%); - left: 2px; - bottom: 5px; - top: 5px; - } - } - &:not(.disabled):hover:before { - @include trans-prop-nice("border-color", 25ms); - border-color: $colorGrippyInteriorHover; + } @else if $direction == "vertical" { + border-left: $w $style darken($b, 15%); + left: 2px; + bottom: 5px; + top: 5px; + width: 1px; } } @@ -256,6 +275,12 @@ @return percentage($d); } +@function splitterHandleInset($splitterD: 21px, $splitterHandleD: 1px) { + // Space to either side of the handle + @return ($splitterD - $splitterHandleD) * 0.5; +} + + /*********************************************** CONTROLS, FORM ELEMENTS */ @mixin containerBase($bg: $colorBodyBg, $fg: $colorBodyFg) { @@ -347,10 +372,7 @@ /* This doesn't work on an element inside an element with absolute positioning that has height: auto */ //position: relative; top: 50%; - @include webkitProp(transform, translateY(-50%)); - //-webkit-transform: translateY(-50%); - //-ms-transform: translateY(-50%); - //transform: translateY(-50%); + @include transform(translateY(-50%)); } @mixin verticalCenterBlock($holderH, $itemH) { diff --git a/platform/commonUI/general/res/sass/controls/_buttons.scss b/platform/commonUI/general/res/sass/controls/_buttons.scss index abf191cece..956e144899 100644 --- a/platform/commonUI/general/res/sass/controls/_buttons.scss +++ b/platform/commonUI/general/res/sass/controls/_buttons.scss @@ -22,13 +22,17 @@ $baseRatio: 1.5; $pad: $interiorMargin * $baseRatio; -.s-btn { - @include box-sizing(border-box); +.s-btn, +.s-icon-btn { @include user-select(none); cursor: pointer; text-decoration: none; height: $btnStdH; line-height: $btnStdH; +} + +.s-btn { + @include box-sizing(border-box); padding: 0 $pad; font-size: 0.7rem; @@ -89,6 +93,155 @@ $pad: $interiorMargin * $baseRatio; } } +.s-icon-btn { + @extend .ui-symbol; + color: $colorBtnIcon; + &:hover { + color: lighten($colorBtnIcon, $ltGamma); + } +} + +.mini-tab { + // Meant to be used as pane hide/show control elements in concert with mct-splitter + //@extend .ui-symbol; + @include desktop { + //@include test(green); + $iconH: $uePaneMiniTabH; + $iconW: $uePaneMiniTabW; + $iconInnerLR: 0; + $arwD: 9px; + $arwOffsetX: 0px; + $arwAnimOffsetX: 2px + $iconInnerLR; + $cBg: pullForward($colorBodyBg, 15%); + $cFg: $cBg; + + + @include border-radius($basicCr); + //@include boxShdw($shdwBtns); + @include box-sizing(border-box); + @include trans-prop-nice((color, background-color), 100ms); + color: $cFg; + cursor: pointer; + font-family: symbolsfont; + font-size: $arwD; + display: block; + position: absolute; + line-height: $iconH; + height: $iconH; width: $iconW; + text-align: center; + + &:hover { + //background-color: $cBg; + color: $colorKey; //pullForward($cFg, $ltGamma); + } + + &.collapsed { + // State when the pane this element controls has been collapsed + @include btnSubtle($colorBtnBg, $colorKey, $colorBtnFg, $colorBtnIcon); + &:before { opacity: 0; } + &:after { opacity: 1; } + &:hover { + &:before { opacity: 1; } + &:after { opacity: 0; } + } + + } + + &:before, + &:after { + //@include test(); + @include trans-prop-nice((left, right, opacity), 250ms); + display: block; + height: 100%; + position: absolute; + } + + &:before { + // Always the arrow icon + //@include test(green); + //font-size: $arwD; + width: $arwD; + } + &:after { + // Always icon; content is set in _layout.scss + width: 100%; + text-align: center; + opacity: 0; + } + + &.anchor-left { + // |< + text-align: right; + &:before { + content:'\3c'; // Collapse left icon e613 + right: $iconInnerLR; + } + //&:hover:before { right: $arwAnimOffsetX; } + &.collapsed { + @include border-left-radius(0); + text-align: left; + &:before { + content:'\3e'; + left: $iconInnerLR; + } + &:hover:before { left: $arwAnimOffsetX; } + } + } + &.anchor-right { + // >| + text-align: left; + &:before { + content:'\3e'; // Collapse right icon e614 + left: $iconInnerLR; + } + //&:hover:before { left: $arwAnimOffsetX; } + &.collapsed { + @include border-right-radius(0); + &:before { + text-align: right; + content:'\3c'; + right: $iconInnerLR; + } + &:hover:before { right: $arwAnimOffsetX; } + } + } + } +} + +.mini-tab-icon { + // Meant to be used as pane hide/show control elements in concert with mct-splitter + //@extend .ui-symbol; + @include desktop { + $d: $uePaneMiniTabW; + //@include trans-prop-nice(transform, 150ms); + color: pullForward($colorBodyBg, 15%); + cursor: pointer; + display: block; + font-family: symbolsfont; + font-size: $d; + position: absolute; + height: $d; width: $d; + line-height: $d; + overflow: hidden; + word-break: break-all; + + &:before, + &:after { + position: absolute; + display: inherit; + } + + &:before { + content: '\78'; // X icon + } + + &:hover { + color: $colorKey; + //@include transform(scale(1.2)); + } + } +} + .l-btn-set { // Buttons that have a very tight conceptual grouping - no internal space between them. // Structure: .btn-set > mct-representation class=first|last > .s-btn diff --git a/platform/commonUI/general/res/sass/controls/_controls.scss b/platform/commonUI/general/res/sass/controls/_controls.scss index 633952c7bc..a8c1f68aa3 100644 --- a/platform/commonUI/general/res/sass/controls/_controls.scss +++ b/platform/commonUI/general/res/sass/controls/_controls.scss @@ -19,456 +19,507 @@ * this source code distribution or the Licensing information page available * at runtime from the About dialog for additional information. *****************************************************************************/ -/*.control { - // UNUSED? - &.view-control { - .icon { - display: inline-block; - margin: -1px 5px 1px 2px; - vertical-align: middle; - &.triangle-down { - margin: 2px 2px -2px 0px; - } - } - - .label { - display: inline-block; - font-size: 11px; - vertical-align: middle; - } - - .toggle { - @include border-radius(3px); - display: inline-block; - padding: 1px 6px 4px 4px; - &:hover { - background: rgba(white, 0.1); - } - } - } -}*/ .accordion { - $accordionHeadH: 18px; - margin-top: $interiorMargin; - &:first-child { - margin-top: 0; - } - .accordion-head { - $op: 0.2; - @include border-radius($basicCr * 0.75); - @include box-sizing("border-box"); - background: rgba($colorBodyFg, $op); - cursor: pointer; - font-size: 0.75em; - line-height: $accordionHeadH; - margin-bottom: $interiorMargin; - padding: 0 $interiorMargin; - position: absolute; - top: 0; - right: 0; - bottom: auto; - left: 0; - width: auto; - height: $accordionHeadH; - text-transform: uppercase; - &:hover { - background: rgba($colorBodyFg, $op * 2); - } - &:after { - content: "^"; - display: block; - font-family: 'symbolsfont'; - font-size: 0.9em; - position: absolute; - right: $interiorMargin; - text-transform: none; - top: 0; - } - &:not(.expanded):after { - content: "v"; - } - } - .accordion-contents { - position: absolute; - top: $accordionHeadH + $interiorMargin; - right: 0; - bottom: 0; - left: 0; - overflow-y: auto; - overflow-x: hidden; - } + $accordionHeadH: 18px; + margin-top: $interiorMargin; + &:first-child { + margin-top: 0; + } + .accordion-head { + $op: 0.2; + @include border-radius($basicCr * 0.75); + @include box-sizing("border-box"); + background: rgba($colorBodyFg, $op); + cursor: pointer; + font-size: 0.75em; + line-height: $accordionHeadH; + margin-bottom: $interiorMargin; + padding: 0 $interiorMargin; + position: absolute; + top: 0; + right: 0; + bottom: auto; + left: 0; + width: auto; + height: $accordionHeadH; + text-transform: uppercase; + &:hover { + background: rgba($colorBodyFg, $op * 2); + } + &:after { + content: "^"; + display: block; + font-family: 'symbolsfont'; + font-size: 0.9em; + position: absolute; + right: $interiorMargin; + text-transform: none; + top: 0; + } + &:not(.expanded):after { + content: "v"; + } + } + .accordion-contents { + position: absolute; + top: $accordionHeadH + $interiorMargin; + right: 0; + bottom: 0; + left: 0; + overflow-y: auto; + overflow-x: hidden; + } } .l-composite-control { - vertical-align: middle; - &.l-checkbox { - .composite-control-label { - line-height: 18px; - } - } + vertical-align: middle; + &.l-checkbox { + .composite-control-label { + line-height: 18px; + } + } } .l-control-group { - // Buttons that have a conceptual grouping - internal space between, and a divider between groups. - // @include test(); - @include box-sizing(border-box); - border-left: 1px solid $colorInteriorBorder; - display: inline-block; - padding: 0 $interiorMargin; - position: relative; - &:first-child { - border-left: none; - padding-left: 0; - } + // Buttons that have a conceptual grouping - internal space between, and a divider between groups. + // @include test(); + @include box-sizing(border-box); + border-left: 1px solid $colorInteriorBorder; + display: inline-block; + padding: 0 $interiorMargin; + position: relative; + &:first-child { + border-left: none; + padding-left: 0; + } } .l-local-controls { - // Control shown when hovering over an object, like plots and imagery - // Default position is upper right - $p: $interiorMargin; - position: absolute; - top: $p; - right: $p; - z-index: 5; + // Control shown when hovering over an object, like plots and imagery + // Default position is upper right + $p: $interiorMargin; + position: absolute; + top: $p; + right: $p; + z-index: 5; } .s-local-controls { - font-size: 0.7rem; + font-size: 0.7rem; } label.checkbox.custom { - $bg: pullForward($colorBodyBg, 10%); - $d: $formRowCtrlsH; - cursor: pointer; - display: inline-block; - line-height: $d; - margin-right: $interiorMargin * 4; - padding-left: $d + $interiorMargin; - position: relative; - vertical-align: middle; // was top - em { - color: $colorBodyFg; - display: inline-block; - height: $d; - min-width: $d; - &:before { - @include border-radius($basicCr * .75); - background: $bg; - //border-bottom: 1px solid lighten($bg, 10%); - @include box-shadow(inset rgba(black, 0.4) 0 1px 2px); - box-sizing: border-box; - content: " "; - font-family: 'symbolsfont'; - font-size: 0.8em; - display: inline-block; - margin-right: $interiorMargin; - height: $d; - width: $d; - left: 0; - top: 0; - position: absolute; - text-align: center; - } - } - &.no-text { - overflow: hidden; - margin-right: 0; - padding-left: 0; - height: $d; - width: $d; - em { - overflow: hidden; - } - } - input { - display: none; - &:checked ~ em:before { - background: $colorCheck; - color: lighten($colorCheck, 50%); - content: "2"; - } - } + $bg: pullForward($colorBodyBg, 10%); + $d: $formRowCtrlsH; + cursor: pointer; + display: inline-block; + line-height: $d; + margin-right: $interiorMargin * 4; + padding-left: $d + $interiorMargin; + position: relative; + vertical-align: middle; // was top + em { + color: $colorBodyFg; + display: inline-block; + height: $d; + min-width: $d; + &:before { + @include border-radius($basicCr * .75); + background: $bg; + //border-bottom: 1px solid lighten($bg, 10%); + @include box-shadow(inset rgba(black, 0.4) 0 1px 2px); + box-sizing: border-box; + content: " "; + font-family: 'symbolsfont'; + font-size: 0.8em; + display: inline-block; + margin-right: $interiorMargin; + height: $d; + width: $d; + left: 0; + top: 0; + position: absolute; + text-align: center; + } + } + &.no-text { + overflow: hidden; + margin-right: 0; + padding-left: 0; + height: $d; + width: $d; + em { + overflow: hidden; + } + } + input { + display: none; + &:checked ~ em:before { + background: $colorCheck; + color: lighten($colorCheck, 50%); + content: "2"; + } + } } .input-labeled { - margin-left: $interiorMargin; - label { - display: inline-block; - margin-right: $interiorMarginSm; - } - &.inline { - display: inline-block; - } - &:first-child { - margin-left: 0; - } + margin-left: $interiorMargin; + label { + display: inline-block; + margin-right: $interiorMarginSm; + } + &.inline { + display: inline-block; + } + &:first-child { + margin-left: 0; + } } .s-menu-btn label.checkbox.custom { - margin-left: 5px; + margin-left: 5px; } .item .checkbox { - &.checked label { - @include box-shadow(none); - border-bottom: none; - } + &.checked label { + @include box-shadow(none); + border-bottom: none; + } } .context-available { - $c: $colorKey; - color: $c; - &:hover { - color: lighten($c, 10%); - } + $c: $colorKey; + color: $c; + &:hover { + color: lighten($c, 10%); + } } .view-switcher { - @include trans-prop-nice-fade($controlFadeMs); + @include trans-prop-nice-fade($controlFadeMs); } /******************************************************** OBJECT-HEADER */ .object-header { - //@include test(); - font-size: 1em; + //@include test(); + font-size: 1em; - > .type-icon { - color: $colorObjHdrIc; - font-size: 120%; - float: left; - margin-right: $interiorMargin; - } + > .type-icon { + color: $colorObjHdrIc; + font-size: 120%; + float: left; + margin-right: $interiorMargin; + } - .l-elem-wrapper { - //@include test(#66f, 0.2); - //@include webkitProp(justify-content, flex-start); + .l-elem-wrapper { + //@include test(#66f, 0.2); @include justify-content(flex-start); - mct-representation { - // Holds the context-available item - // Must have min-width to make flex work properly - // in Safari - min-width: 0.7em; - } - } + mct-representation { + // Holds the context-available item + // Must have min-width to make flex work properly + // in Safari + min-width: 0.7em; + } + } - .action { - margin-right: $interiorMargin; - } + .action { + margin-right: $interiorMargin; + } - .title-label { - //@include test(green, 0.9); - color: $colorObjHdrTxt; - @include ellipsize(); - //color: pushBack($colorBodyFg, 40%); - @include webkitProp(flex, '0 1 auto'); - padding-right: 0.35em; // For context arrow. Done with em's so pad is relative to the scale of the text. - //position: relative; - } + .title-label { + //@include test(green, 0.9); + color: $colorObjHdrTxt; + @include ellipsize(); + //color: pushBack($colorBodyFg, 40%); + @include webkitProp(flex, '0 1 auto'); + padding-right: 0.35em; // For context arrow. Done with em's so pad is relative to the scale of the text. + //position: relative; + } - .context-available { - font-size: 0.7em; - @include webkitProp(flex, '0 0 1'); - //margin-right: $interiorMargin; - } + .context-available { + font-size: 0.7em; + @include webkitProp(flex, '0 0 1'); + //margin-right: $interiorMargin; + } - @include desktop { - .context-available { - @include trans-prop-nice(opacity, 0.25s); - opacity: 0; - } - &:hover { - .context-available { - opacity: 1; - } - } - } + @include desktop { + .context-available { + @include trans-prop-nice(opacity, 0.25s); + opacity: 0; + } + &:hover { + .context-available { + opacity: 1; + } + } + } +} + +/******************************************************** PROGRESS BAR */ +@include keyframes(progress) { + 100% { background-position: $progressBarStripeW center; } +} + +@mixin bgProgressAnim($c: yellow, $a: 0.1, $d: 20px) { + @include background-image(linear-gradient(-90deg, + rgba($c, $a) 0%, transparent 50%, + transparent 50%, rgba($c, $a) 100% + )); + background-position: 0 center; + background-repeat: repeat-x; + background-size: $d 40%; +} + +.l-progress-bar { + // Assume will be determinate by default + display: inline-block; + overflow: hidden; + position: relative; + + .progress-amt-holder { + @include absPosDefault(1px); + } + .progress-amt, + .progress-amt:before, + .progress-amt:after { + @include absPosDefault(); + display: block; + content: ''; + } + + .progress-amt { + right: auto; // Allow inline width to control } + } + + &.indeterminate { + .progress-amt { + width: 100% !important; + } + } +} + +.s-progress-bar { + @include border-radius($basicCr); + @include boxIncised(0.3, 4px); + background: $colorProgressBarOuter; + //border:1px solid $colorProgressBarOuter; + .progress-amt { + @include border-radius($basicCr); + @include boxShdw(); + @include border-radius($basicCr - 1); + @include trans-prop-nice(width); + &:before { + background-color: $colorProgressBarAmt; + } + &:after { + // Sheen + @include background-image(linear-gradient( + transparent 5%, rgba(#fff,0.25) 30%, transparent 100% + )); + } + } + + &:not(.indeterminate) { + .progress-amt:before { + // More subtle anim for determinate progress + @include animation(progress .4s linear infinite); + @include bgProgressAnim(#fff, 0.1, $progressBarStripeW); + } + } + + &.indeterminate .progress-amt { + &:before { + // More visible std diag stripe anim for indeterminate progress + @include animation(progress .6s linear infinite); + @include bgDiagonalStripes(#fff, 0.2, $progressBarStripeW); + } + &:after { display: none; } + } } /******************************************************** SLIDERS */ .slider { - $knobH: 100%; //14px; - .slot { - // @include border-radius($basicCr * .75); - //@include sliderTrack(); - width: auto; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - } - .knob { - //@include btnSubtle(); - //@include controlGrippy(rgba(black, 0.3), vertical, 1px, solid); - @include trans-prop-nice-fade(.25s); - background-color: $sliderColorKnob; - &:hover { - background-color: $sliderColorKnobHov; - } - position: absolute; - height: $knobH; - width: $sliderKnobW; - top: 0; - auto: 0; - bottom: auto; - left: auto; - } - .knob-l { - @include border-left-radius($sliderKnobW); - cursor: w-resize; - } - .knob-r { - @include border-right-radius($sliderKnobW); - cursor: e-resize; - } - .range { - @include trans-prop-nice-fade(.25s); - background-color: $sliderColorRange; - cursor: ew-resize; - position: absolute; - top: 0; //$tbOffset; - right: auto; - bottom: 0; - left: auto; - height: auto; - width: auto; - &:hover { - background-color: $sliderColorRangeHov; - } - } + $knobH: 100%; //14px; + .slot { + // @include border-radius($basicCr * .75); + //@include sliderTrack(); + width: auto; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + } + .knob { + @include trans-prop-nice-fade(.25s); + background-color: $sliderColorKnob; + &:hover { + background-color: $sliderColorKnobHov; + } + position: absolute; + height: $knobH; + width: $sliderKnobW; + top: 0; + auto: 0; + bottom: auto; + left: auto; + } + .knob-l { + @include border-left-radius($sliderKnobW); + cursor: w-resize; + } + .knob-r { + @include border-right-radius($sliderKnobW); + cursor: e-resize; + } + .range { + @include trans-prop-nice-fade(.25s); + background-color: $sliderColorRange; + cursor: ew-resize; + position: absolute; + top: 0; //$tbOffset; + right: auto; + bottom: 0; + left: auto; + height: auto; + width: auto; + &:hover { + background-color: $sliderColorRangeHov; + } + } } /******************************************************** DATETIME PICKER */ .l-datetime-picker { - $r1H: 15px; - @include user-select(none); - font-size: 0.8rem; - padding: $interiorMarginLg !important; - width: 230px; - .l-month-year-pager { - $pagerW: 20px; - //@include test(); - //font-size: 0.8rem; - height: $r1H; - margin-bottom: $interiorMargin; - position: relative; - .pager, - .val { - //@include test(red); - @extend .abs; - } - .pager { - width: $pagerW; - @extend .ui-symbol; - &.prev { - right: auto; - &:before { - content: "\3c"; - } - } - &.next { - left: auto; - text-align: right; - &:before { - content: "\3e"; - } - } - } - .val { - text-align: center; - left: $pagerW + $interiorMargin; - right: $pagerW + $interiorMargin; - } - } - .l-calendar, - .l-time-selects { - border-top: 1px solid $colorInteriorBorder - } - .l-time-selects { - line-height: $formInputH; - } + $r1H: 15px; + @include user-select(none); + font-size: 0.8rem; + padding: $interiorMarginLg !important; + width: 230px; + .l-month-year-pager { + $pagerW: 20px; + //@include test(); + //font-size: 0.8rem; + height: $r1H; + margin-bottom: $interiorMargin; + position: relative; + .pager, + .val { + //@include test(red); + @extend .abs; + } + .pager { + width: $pagerW; + @extend .ui-symbol; + &.prev { + right: auto; + &:before { + content: "\3c"; + } + } + &.next { + left: auto; + text-align: right; + &:before { + content: "\3e"; + } + } + } + .val { + text-align: center; + left: $pagerW + $interiorMargin; + right: $pagerW + $interiorMargin; + } + } + .l-calendar, + .l-time-selects { + border-top: 1px solid $colorInteriorBorder + } + .l-time-selects { + line-height: $formInputH; + } } /******************************************************** CALENDAR */ .l-calendar { - $colorMuted: pushBack($colorMenuFg, 30%); - ul.l-cal-row { - @include display-flex; - @include flex-flow(row nowrap); - margin-top: 1px; - &:first-child { - margin-top: 0; - } - li { - @include flex(1 0); - //@include test(); - margin-left: 1px; - padding: $interiorMargin; - text-align: center; - &:first-child { - margin-left: 0; - } - } - &.l-header li { - color: $colorMuted; - } - &.l-body li { - @include trans-prop-nice(background-color, .25s); - cursor: pointer; - &.in-month { - background-color: $colorCalCellInMonthBg; - } - .sub { - color: $colorMuted; - font-size: 0.8em; - } - &.selected { - background: $colorCalCellSelectedBg; - color: $colorCalCellSelectedFg; - .sub { - color: inherit; - } - } - &:hover { - background-color: $colorCalCellHovBg; - color: $colorCalCellHovFg; - .sub { - color: inherit; - } - } - } - } + $colorMuted: pushBack($colorMenuFg, 30%); + ul.l-cal-row { + @include display-flex; + @include flex-flow(row nowrap); + margin-top: 1px; + &:first-child { + margin-top: 0; + } + li { + @include flex(1 0); + //@include test(); + margin-left: 1px; + padding: $interiorMargin; + text-align: center; + &:first-child { + margin-left: 0; + } + } + &.l-header li { + color: $colorMuted; + } + &.l-body li { + @include trans-prop-nice(background-color, .25s); + cursor: pointer; + &.in-month { + background-color: $colorCalCellInMonthBg; + } + .sub { + color: $colorMuted; + font-size: 0.8em; + } + &.selected { + background: $colorCalCellSelectedBg; + color: $colorCalCellSelectedFg; + .sub { + color: inherit; + } + } + &:hover { + background-color: $colorCalCellHovBg; + color: $colorCalCellHovFg; + .sub { + color: inherit; + } + } + } + } } /******************************************************** BROWSER ELEMENTS */ @include desktop { - ::-webkit-scrollbar { - @include border-radius(2px); - @include box-sizing(border-box); - @include box-shadow(inset $scrollbarTrackShdw); - background-color: $scrollbarTrackColorBg; - height: $scrollbarTrackSize; - width: $scrollbarTrackSize; - } + ::-webkit-scrollbar { + @include border-radius(2px); + @include box-sizing(border-box); + @include box-shadow(inset $scrollbarTrackShdw); + background-color: $scrollbarTrackColorBg; + height: $scrollbarTrackSize; + width: $scrollbarTrackSize; + } - ::-webkit-scrollbar-thumb { - $bg: $scrollbarThumbColor; - $hc: $scrollbarThumbColorHov; - $gr: 5%; - @include background-image(linear-gradient(lighten($bg, $gr), $bg 20px)); - @include border-radius(2px); - @include box-sizing(border-box); - //@include boxShdwSubtle(); - //border-top: 1px solid lighten($bg, 20%); - &:hover { - @include background-image(linear-gradient(lighten($hc, $gr), $hc 20px)); - } - } + ::-webkit-scrollbar-thumb { + $bg: $scrollbarThumbColor; + $hc: $scrollbarThumbColorHov; + $gr: 5%; + @include background-image(linear-gradient(lighten($bg, $gr), $bg 20px)); + @include border-radius(2px); + @include box-sizing(border-box); + //@include boxShdwSubtle(); + //border-top: 1px solid lighten($bg, 20%); + &:hover { + @include background-image(linear-gradient(lighten($hc, $gr), $hc 20px)); + } + } - ::-webkit-scrollbar-corner { - background: $scrollbarTrackColorBg; - } -} \ No newline at end of file + ::-webkit-scrollbar-corner { + background: $scrollbarTrackColorBg; + } +} diff --git a/platform/commonUI/general/res/sass/controls/_lists.scss b/platform/commonUI/general/res/sass/controls/_lists.scss index d19f91ef47..accae0583f 100644 --- a/platform/commonUI/general/res/sass/controls/_lists.scss +++ b/platform/commonUI/general/res/sass/controls/_lists.scss @@ -32,7 +32,7 @@ .l-tree-item-flat-list { // For lists of tree-items that are flat. Remove margin, etc. normally needed for the expansion arrow. .tree-item { - .label { + .t-object-label { left: $interiorMargin !important; } } diff --git a/platform/commonUI/general/res/sass/controls/_menus.scss b/platform/commonUI/general/res/sass/controls/_menus.scss index 49693b3c59..473e9f1d45 100644 --- a/platform/commonUI/general/res/sass/controls/_menus.scss +++ b/platform/commonUI/general/res/sass/controls/_menus.scss @@ -43,6 +43,11 @@ } &.create-btn { + &:before { + content:'\2b'; + display: inline; + font-family: symbolsfont; + } .title-label { font-size: 1rem; } @@ -83,7 +88,7 @@ @include menuUlReset(); li { @include box-sizing(border-box); - border-top: 1px solid lighten($colorMenuBg, 20%); + border-top: 1px solid pullForward($colorMenuBg, 10%); color: pullForward($colorMenuBg, 60%); line-height: $menuLineH; padding: $interiorMarginSm $interiorMargin * 2 $interiorMarginSm ($interiorMargin * 2) + $treeTypeIconW; diff --git a/platform/commonUI/general/res/sass/controls/_messages.scss b/platform/commonUI/general/res/sass/controls/_messages.scss new file mode 100644 index 0000000000..740df6ba8d --- /dev/null +++ b/platform/commonUI/general/res/sass/controls/_messages.scss @@ -0,0 +1,306 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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. + *****************************************************************************/ + +@mixin statusBannerColors($bg, $fg: $colorStatusFg) { + $bgPb: 30%; + $bgPbD: 10%; + background-color: darken($bg, $bgPb); + color: $fg; + &:hover { + background-color: darken($bg, $bgPb - $bgPbD); + } + .s-action { + background-color: darken($bg, $bgPb + $bgPbD); + &:hover { + background-color: darken($bg, $bgPb); + } + } +} + +.status.block { + color: $colorStatusDefault; + cursor: default; + display: inline-block; + margin-right: $interiorMargin; + .status-indicator, + .label, + .count { + //@include test(#00ff00); + display: inline-block; + vertical-align: top; + } + .status-indicator { + margin-right: $interiorMarginSm; + } + &.ok .status-indicator, + &.info .status-indicator { + color: $colorStatusInfo; + } + &.alert .status-indicator, + &.warning .status-indicator, + &.caution .status-indicator { + color: $colorStatusAlert; + } + &.error .status-indicator { + color: $colorStatusError; + } + .label { + // Max-width silliness is necessary for width transition + @include trans-prop-nice(max-width, .25s); + overflow: hidden; + max-width: 0px; + } + .count { + @include trans-prop-nice(opacity, .25s); + font-weight: bold; + opacity: 1; + } + &:hover { + .label { + max-width: 450px; + width: auto; + } + .count { + opacity: 0; + } + } +} + +/* Styles for messages and message banners */ +.message { + &.block { + @include border-radius($basicCr); + padding: $interiorMarginLg; + } + &.error { + background-color: rgba($colorAlert,0.3); + color: lighten($colorAlert, 20%); + } +} + +.l-message-banner { + $m: $interiorMarginSm; + $lh: $ueFooterH - ($m*2) - 1; + @include box-sizing(border-box); + @include ellipsize(); + @include display-flex; + @include flex-direction(row); + @include align-items(center); + position: absolute; + top: $m; right: auto; bottom: $m; left: 50%; + height: auto; width: auto; + line-height: $lh; + max-width: 300px; + padding: 0 $interiorMargin 0 $interiorMargin; + @include transform(translateX(-50%)); + + &.minimized { + @include transition-property(left, opacity); + @include transition-duration(0.3s); + @include transition-timing-function(ease-in-out); + left: 0; + opacity: 0; + } + + &.new { + left: 50%; + opacity: 1; + &:not(.info) { + @include pulse(100ms, 10); + } + } + + .banner-elem { + @include flex(0 1 auto); + margin-left: $interiorMargin; + } + a { + display: inline-block; + } + .l-action { + line-height: $lh - 3; + padding: 0 $interiorMargin; + } + .close { + //@include test(red, 0.7); + cursor: pointer; + font-size: 7px; + width: 8px; + } + .l-progress-bar { + $h: $lh - 10; + height: $h; + line-height: $h; + width: 100px; + } + .progress-info { display: none; } + z-index: 10; +} + +.s-message-banner { + //@include transition-property(left, opacity); + //@include transition-duration(0.35s); + //@include transition-timing-function(ease-in-out); +} + +.s-message-banner { + @include border-radius($controlCr); + @include statusBannerColors($colorStatusDefault, $colorStatusFg); + cursor: pointer; + a { color: inherit; } + .s-action { + @include border-radius($basicCr); + @include trans-prop-nice(background-color); + } + .close { + opacity: 0.5; + &:hover { + opacity: 1; + } + } + &.ok, + &.info { + @include statusBannerColors($colorStatusInfo); + } + &.caution, + &.warning, + &.alert { + @include statusBannerColors($colorStatusAlert); + } + &.error { + @include statusBannerColors($colorStatusError); + } +} + +@mixin messageBlock($iconW: 32px) { + .type-icon.message-type { + @include txtShdw($shdwStatusIc); + &:before { content:"\e608"; } + color: $colorStatusDefault; + font-size: $iconW; + padding: 1px; + width: $iconW + 2; + } + + .message-severity-info .type-icon.message-type { + &:before { content:"\e608"; } + color: $colorStatusInfo; + } + .message-severity-alert .type-icon.message-type { + &:before { content:"\e610"; } + color: $colorStatusAlert; + } + .message-severity-error .type-icon.message-type { + &:before { content:"\21"; } + color: $colorStatusError; + } +} +/* Paths: + t-dialog | t-dialog-sm > t-message-single | t-message-list > overlay > holder > contents > l-message > + message-type > (icon) + message-contents > + top-bar > + title + hint + editor > + (if displaying list of messages) + ul > li > l-message > + ... same as above + bottom-bar +*/ + +.l-message { + @include display-flex; + @include flex-direction(row); + @include align-items(stretch); + .type-icon.message-type { + //@include test(red); + @include flex(0 1 auto); + position: relative; + } + .message-contents { + //@include test(blue); + @include flex(1 1 auto); + margin-left: $overlayMargin; + position: relative; + + .top-bar, + .message-body { + margin-bottom: $interiorMarginLg * 2; + } + } +} + + +// Message as singleton +.t-message-single { + @include messageBlock(80px); + + @include desktop { + .l-message, + .bottom-bar { + @include absPosDefault(); + } + + .bottom-bar { + top: auto; + height: $ovrFooterH; + } + } +} + +// Messages in list +.t-message-list { + @include messageBlock(32px); + + .message-contents { + .l-message { + //border-bottom: 1px solid pullForward($colorOvrBg, 20%); + @include border-radius($controlCr); + background: rgba($colorOvrFg, 0.1); + margin-bottom: $interiorMargin; + padding: $interiorMarginLg; + + .message-contents, + .bottom-bar { + //@include test(green); + position: relative; + } + + .message-contents { + font-size: 0.9em; + margin-left: $interiorMarginLg; + .message-action { color: pushBack($colorOvrFg, 20%); } + .bottom-bar { text-align: left; } + } + + .top-bar, + .message-body { + margin-bottom: $interiorMarginLg; + } + } + } + + @include desktop { + .message-contents .l-message { margin-right: $interiorMarginLg; } + } +} \ No newline at end of file diff --git a/platform/commonUI/general/res/sass/helpers/_splitter.scss b/platform/commonUI/general/res/sass/helpers/_splitter.scss index f91f5936d4..a98fc28af5 100644 --- a/platform/commonUI/general/res/sass/helpers/_splitter.scss +++ b/platform/commonUI/general/res/sass/helpers/_splitter.scss @@ -19,68 +19,114 @@ * this source code distribution or the Licensing information page available * at runtime from the About dialog for additional information. *****************************************************************************/ -.split-layout { - $b: pullForward($colorBodyBg, $contrastRatioPercent); - .splitter { - background-color: $b; - @include border-radius($splitterEndCr); - @include boxShdw($splitterShdw); - overflow: hidden; - position: absolute; - z-index: 1; - //@if $colorSplitterHover != 'none' { - &:hover { - background-color: $colorSplitterHover; - } - //} - } - &.horizontal { - // Slides vertically up and down, splitting the element horizontally - overflow: hidden; // Suppress overall scroll; each internal pane handles its own overflow - .pane { - left: 0; - right: 0; - &.top { - bottom: auto; - } - &.bottom { - top: auto; - } - } - >.splitter { - @include controlGrippy($colorSplitterInterior, horizontal); - cursor: row-resize; - left: 0; right: 0; - width: auto; - height: $splitterW; - } - } - &.vertical { - // Slides horizontally left to right, splitting the element vertically - .pane { - top: 0; - bottom: 0; - &.left { - right: auto; - } - &.right { - left: auto; - } - } - >.splitter { - @include controlGrippy($colorBodyBg, vertical); - bottom: 0; - cursor: col-resize; - width: $splitterW; - } - } +.splitter { + // Redo the splitter. + // New look is a simple line. + // Main width is used to provide a good click area, and is always transparent + // :after will be a positioned and colored element that is the handle + + //@include test(red); + display: block; + position: absolute; + z-index: 1; + &:after { + // The handle + content:""; + pointer-events: none; + @include absPosDefault(0); + background: $colorSplitterBg; + display: block; + + @if $splitterEndCr != 'none' { + @include border-radius($splitterEndCr); + } + } + &:active { + //@include test(); + &:after { + background-color: $colorSplitterActive !important; + } + } + + @if $colorSplitterHover != 'none' { + &:not(:active) { + &:hover { + &:after { + background-color: $colorSplitterHover !important; + @include trans-prop-nice(background-color, 150ms); + } + } + } + } } -.browse-area .splitter { - top: $ueTopBarH + $interiorMarginLg; +.split-layout { + $inset: splitterHandleInset($splitterD,$splitterHandleD); + &.horizontal { + // Slides vertically up and down, splitting the element horizontally + overflow: hidden; // Suppress overall scroll; each internal pane handles its own overflow + .pane { + left: 0; + right: 0; + &.top { + bottom: auto; + } + &.bottom { + top: auto; + } + } + >.splitter { + cursor: row-resize; + left: 0; + right: 0; + height: $splitterD; + &:after { + top: $inset; bottom: $inset; + } + } + } + + &.vertical { + // Slides horizontally left to right, splitting the element vertically + .pane { + top: 0; + bottom: 0; + &.left { + right: auto; + } + &.right { + left: auto; + } + } + >.splitter { + cursor: col-resize; + top: 0; + bottom: 0; + &:not(.flush-right) { + width: $splitterD; + &:after { + left: $inset; right: $inset; + } + } + &.flush-right { + width: ceil($splitterD / 2); + &:after { + background-color: transparent; + left: auto; right: 0; width: $splitterHandleD; + } + &.edge-shdw { + @include background-image(linear-gradient(90deg, rgba(black, 0) 40%, rgba(black, 0.05) 70%, rgba(black, 0.2) 100%)); + } + } + } + } +} + +/*.browse-area .splitter { + top: 0; //$ueTopBarH + $interiorMarginLg; } .edit-area .splitter { top: 0; -} +}*/ diff --git a/platform/commonUI/general/res/sass/items/_item.scss b/platform/commonUI/general/res/sass/items/_item.scss index 9e5fe1cf47..d9d068cf90 100644 --- a/platform/commonUI/general/res/sass/items/_item.scss +++ b/platform/commonUI/general/res/sass/items/_item.scss @@ -86,27 +86,16 @@ //top: $ueBrowseGridItemTopBarH; bottom: $ueBrowseGridItemBottomBarH; // line-height: $lh; z-index: 1; - .item-type { - //@include trans-prop-nice("color", $transTime); - @include absPosDefault($iconMargin, false); - //@include test(red); - //color: $colorItemIc; - text-align: center; + .item-type, + .t-item-icon { + //@include test(); + @include transform(translateX(-50%) translateY(-55%)); + position: absolute; + top: 50%; left: 50%; + //height: $iconD; width: $iconD; font-size: $iconD * 0.95; //6em; - line-height: $iconD; - bottom: auto; - height: $iconD; - top: $iconMargin - 10; - .l-icon-link { - color: $colorIconLink; - height: auto; - line-height: 100%; - position: absolute; - font-size: 0.3em; - left: 0px; - bottom: 10px; - z-index: 2; - } + //line-height: normal; + //text-align: center; } .item-open { @include trans-prop-nice("opacity", $transTime); diff --git a/platform/commonUI/general/res/sass/mobile/_constants.scss b/platform/commonUI/general/res/sass/mobile/_constants.scss index bd9443cc50..067bbef6ca 100644 --- a/platform/commonUI/general/res/sass/mobile/_constants.scss +++ b/platform/commonUI/general/res/sass/mobile/_constants.scss @@ -23,7 +23,7 @@ /************************** MOBILE REPRESENTATION ITEMS DIMENSIONS */ $mobileListIconSize: 30px; $mobileTitleDescH: 35px; -$mobileOverlayMargin: 10px; +$mobileOverlayMargin: 20px; $phoneItemH: floor($ueBrowseGridItemLg/4); $tabletItemH: floor($ueBrowseGridItemLg/3); diff --git a/platform/commonUI/general/res/sass/mobile/_item.scss b/platform/commonUI/general/res/sass/mobile/_item.scss index 717e589cb5..e60e96c5a7 100644 --- a/platform/commonUI/general/res/sass/mobile/_item.scss +++ b/platform/commonUI/general/res/sass/mobile/_item.scss @@ -49,18 +49,11 @@ } .item-main { - .item-type { - //@include test(blue); + .item-type, + .t-item-icon { font-size: $mobileListIconSize; - right: auto; - bottom: auto; - left: 0; - line-height: 100%; - text-align: left; - width: $mobileListIconSize; - .l-icon-link { - bottom: 0; - } + left: $interiorMarginLg + $interiorMargin; + line-height: normal; } .item-open { display: block; diff --git a/platform/commonUI/general/res/sass/mobile/_layout.scss b/platform/commonUI/general/res/sass/mobile/_layout.scss index 17ff8c4213..589d7e7c90 100644 --- a/platform/commonUI/general/res/sass/mobile/_layout.scss +++ b/platform/commonUI/general/res/sass/mobile/_layout.scss @@ -32,7 +32,7 @@ background-color: $colorMobilePaneLeft; } - .pane.right-repr { + .pane.right.items { //@include test(); @include slMenuTransitions; margin-left: 0 !important; @@ -42,78 +42,66 @@ } } - .user-environ .browse-area, - .user-environ .edit-area, - .user-environ .editor { - top: 0; left: 0; right: 0; bottom: $ueFooterH; - } - - .holder.l-mobile { - top: $bodyMargin !important; + .holder.holder-create-and-search { right: $bodyMargin !important; - bottom: $bodyMargin !important; - left: $bodyMargin !important; } - // When the tree is hidden, these are the + +// When the tree is hidden, these are the // classes used for the left menu and the // right representation. - .browse-hidetree { - @include user-select(none); + .pane-tree-hidden { // Sets the left tree menu when the tree // is hidden. .pane.left.treeview { - opacity: 0; - right: 100% !important; - width: auto !important; - overflow-y: hidden; - overflow-x: hidden; + @include trans-prop-nice(opacity, 150ms); + //right: 100% !important; + //width: auto !important; + //overflow-y: hidden; + //overflow-x: hidden; + opacity: 0 !important; } - // Sets the right represenation when - // the tree is hidden. - .pane.right-repr { + .pane.right.items { left: 0 !important; } } - .browse-showtree { + .pane-tree-showing { // NOTE: DISABLED SELECTION // Selection disabled in both panes // causing cut/copy/paste menu to // not appear. Should me moved in // future to properly work - @include user-select(none); + //@include user-select(none); // Sets the left tree menu when the tree is shown. .pane.left.treeview { - @include trans-prop-nice(opacity, .4s); + @include trans-prop-nice(opacity, 250ms, $delay: 250ms); @include background-image(linear-gradient(90deg, rgba(black, 0) 98%, rgba(black, 0.3) 100%)); - opacity: 1; - display: block !important; - //width: auto !important; // CH CO right: auto !important; width: $proporMenuWithView !important; } // Sets the right representation when the tree is shown. - .pane.right-repr { + .pane.right.items { left: $proporMenuWithView !important; - //width: auto !important; - - //left: 0 !important; - //transform: translateX($proporMenuWithView); } } - .mobile-menu-icon { + .toggle-tree { + color: $colorKey !important; font-size: 110%; position: absolute; top: $bodyMargin + 2; left: $bodyMargin; + &:after { + content:'m' !important; + font-family: symbolsfont; + } } .object-browse-bar { //@include test(); - left: 30px !important; + left: 45px !important; .context-available { opacity: 1 !important; } @@ -153,13 +141,13 @@ } @include phonePortrait { - .browse-showtree { + .pane-tree-showing { .pane.left.treeview { width: $proporMenuOnly !important; } - .pane.right-repr { + .pane.right.items { left: 0 !important; - @include webkitProp(transform, translateX($proporMenuOnly)); + @include transform(translateX($proporMenuOnly)); #content-area { opacity: 0; } diff --git a/platform/commonUI/general/res/sass/mobile/_tree.scss b/platform/commonUI/general/res/sass/mobile/_tree.scss index e6efb4be55..f3862be742 100644 --- a/platform/commonUI/general/res/sass/mobile/_tree.scss +++ b/platform/commonUI/general/res/sass/mobile/_tree.scss @@ -37,21 +37,18 @@ //@include test(red); position: absolute; font-size: 1.1em; + height: $mobileTreeItemH; + line-height: inherit; right: 0px; width: $mobileTreeRightArrowW; text-align: center; } - .label { + .label, + .t-object-label { left: 0; right: $mobileTreeRightArrowW + $interiorMargin; // Allows tree item name to stop prior to the arrow - line-height: $mobileTreeItemH; - //font-size: 1.1em; // CH CO - .type-icon { - @include verticalCenterBlock($mobileTreeItemH, $treeTypeIconH); - } - .title-label { - } + line-height: inherit; } } } diff --git a/platform/commonUI/general/res/sass/mobile/overlay/_overlay.scss b/platform/commonUI/general/res/sass/mobile/overlay/_overlay.scss index caa6df0967..9fd6721130 100644 --- a/platform/commonUI/general/res/sass/mobile/overlay/_overlay.scss +++ b/platform/commonUI/general/res/sass/mobile/overlay/_overlay.scss @@ -1,16 +1,12 @@ @include phoneandtablet { .overlay { - $m: 0; .clk-icon.close { top: $mobileOverlayMargin; right: $mobileOverlayMargin; } > .holder { - @include border-radius($m); - top: $m; - right: $m; - bottom: $m; - left: $m; + height: 90%; width: 90%; + > .contents { top: $mobileOverlayMargin; right: $mobileOverlayMargin; @@ -22,35 +18,64 @@ margin-right: 1.2em; } } - - .form.editor { - border: none; - - .contents { - top: 0; - right: 0; - bottom: 0; - left: 0; - } - } } } } } @include phone { - .overlay > .holder > .contents .form.editor .contents .form-row { - > .label, - > .controls { - //@include test(blue); - display: block; - float: none; - width: 100%; + .overlay > .holder { + //@include test(orange); // This works! + $m: 0; + @include border-radius($m); + top: $m; + right: $m; + bottom: $m; + left: $m; + height: auto; width: auto; + min-width: 200px; min-height: 200px; + max-height: 100%; max-width: 100%; + overflow: auto; + @include transform(none); + + .editor .form .form-row { + > .label, + > .controls { + //@include test(blue); + display: block; + float: none; + width: 100%; + } + > .label { + &:after { + float: none; + } + } + } + + .contents { + .abs.top-bar, + .abs.editor, + .abs.message-body, + .abs.bottom-bar { + //@include test(orange); + top: auto; right: auto; bottom: auto; left: auto; + height: auto; width: auto; + margin-bottom: $interiorMarginLg * 2; + position: relative; + } + } + } + .t-dialog-sm .overlay > .holder { + //@include test(blue); + height: auto; max-height: 100%; + } +} + +@include phonePortrait { + .overlay > .holder { + .contents .bottom-bar { + text-align: center; } - > .label { - &:after { - float: none; - } - } } } \ No newline at end of file diff --git a/platform/commonUI/general/res/sass/overlay/_overlay.scss b/platform/commonUI/general/res/sass/overlay/_overlay.scss index 2dce492563..602af62887 100644 --- a/platform/commonUI/general/res/sass/overlay/_overlay.scss +++ b/platform/commonUI/general/res/sass/overlay/_overlay.scss @@ -20,79 +20,124 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ .overlay { - .blocker { - background: $colorOvrBlocker; - z-index: 100; - } + font-size: 90%; + .blocker { + background: $colorOvrBlocker; + z-index: 100; + } .clk-icon.close { font-size: 0.8rem; position: absolute; - top: $interiorMarginLg; right: $interiorMarginLg; bottom: auto; left: auto; - z-index: 100; + top: $interiorMarginLg; + right: $interiorMarginLg; + bottom: auto; + left: auto; + z-index: 100; } - >.holder { - $i: 15%; - @include containerSubtle($colorOvrBg, $colorOvrFg); - @include border-radius($basicCr * 3); - color: $colorOvrFg; - top: $i; right: $i; bottom: $i; left: $i; - z-index: 101; - >.contents { + > .holder { + //$i: 15%; + @include containerSubtle($colorOvrBg, $colorOvrFg); + @include border-radius($basicCr * 3); + color: $colorOvrFg; + top: 50%; + right: auto; + bottom: auto; + left: 50%; + @include transform(translateX(-50%) translateY(-50%)); + height: 70%; + width: 50%; + min-height: 300px; + max-height: 800px; + min-width: 600px; + max-width: 1000px; + z-index: 101; + > .contents { $m: $overlayMargin; - top: $m; right: $m; bottom: $m; left: $m; + top: $m; + right: $m; + bottom: $m; + left: $m; + + //.top-bar, + //.editor, + //.bottom-bar { + // @include absPosDefault(); + //} } - } - .title { - @include ellipsize(); - font-size: 1.2em; - margin-bottom: $interiorMargin; } - - .top-bar { + + .title { + @include ellipsize(); + font-size: 1.2em; + line-height: 120%; + margin-bottom: $interiorMargin; + } + + .hint { + color: pushBack($colorOvrFg, 20%); + } + + .abs.top-bar { height: $ovrTopBarH; } - - .editor { - top: $ovrTopBarH + ($interiorMargin * 2); - bottom: $ovrFooterH + $interiorMargin * 2; - left: 0; right: 0; - } - - .bottom-bar { - top: auto; right: 0; bottom: 0; left: 0; - overflow: visible; - //font-size: 1em; - height: $ovrFooterH; - text-align: right; - .s-btn { - $bg: $colorOvrBtnBg; - &:not(.major) { - @include btnSubtle($bg, pullForward($bg, 10%), $colorOvrBtnFg, $colorOvrBtnFg); - } - font-size: 95%; - height: $ovrFooterH; - line-height: $ovrFooterH; - margin-left: $interiorMargin; - padding: 0 $interiorMargin * 3; - //&.major { - // @extend .s-btn.major; - // &:hover { - // @extend .s-btn.major:hover; - // } - //} + + .abs.editor, + .abs.message-body { + top: $ovrTopBarH + $interiorMarginLg; + bottom: $ovrFooterH + $interiorMarginLg; + left: 0; + right: 0; + overflow: auto; + .field.l-med { + input[type='text'] { + width: 100%; + } } } - .contents.l-dialog { - $myM: $interiorMargin; - top: $myM; - right: $myM; - bottom: $myM; - left: $myM; - overflow: auto; - .field.l-med { - input[type='text'] { - width: 100%; - } - } + + .bottom-bar { + text-align: right; + .s-btn { + $bg: $colorOvrBtnBg; + &:not(.major) { + @include btnSubtle($bg, pullForward($bg, 10%), $colorOvrBtnFg, $colorOvrBtnFg); + } + font-size: 95%; + height: $ovrFooterH; + line-height: $ovrFooterH; + margin-left: $interiorMargin; + padding: 0 $interiorMargin * 3; + &:first-child { + margin-left: 0; + } + } + } + + .abs.bottom-bar { + top: auto; + right: 0; + bottom: 0; + left: 0; + overflow: visible; + //font-size: 1em; + height: $ovrFooterH; + } + + .l-progress-bar { + $h: $progressBarHOverlay; + display: block; + height: $h; + line-height: $h; + margin: .5em 0; + width: 100%; + } +} + +.t-dialog-sm .overlay > .holder { + // Used for blocker and in-progress dialogs, modal alerts, etc. + //@include test(red); + $h: 225px; + min-height: $h; + height: $h; } \ No newline at end of file diff --git a/platform/commonUI/general/res/sass/tree/_tree.scss b/platform/commonUI/general/res/sass/tree/_tree.scss index d64f456b2e..a3b9a1a000 100644 --- a/platform/commonUI/general/res/sass/tree/_tree.scss +++ b/platform/commonUI/general/res/sass/tree/_tree.scss @@ -52,7 +52,6 @@ ul.tree { font-size: 0.75em; width: $treeVCW; $runningItemW: $interiorMargin + $treeVCW; - // NOTE: [Mobile] Removed Hover on Mobile @include desktop { &:hover { color: $colorItemTreeVCHover !important; @@ -60,25 +59,34 @@ ul.tree { } } - .label { + .label, + .t-object-label { display: block; - // @include test(orange); @include absPosDefault(); - //left: $runningItemW + $interiorMargin; // Adding pad to left to make room for link icon line-height: $menuLineH; - //left: $runningItemW; + + .t-item-icon { + @include txtShdwSubtle($shdwItemTreeIcon); + font-size: $treeTypeIconH; + color: $colorItemTreeIcon; + position: absolute; + left: $interiorMargin; + top: 50%; + width: $treeTypeIconH; + @include transform(translateY(-50%)); + } .type-icon { //@include absPosDefault(0, false); - $d: $treeTypeIconH; // 16px is crisp size + $d: $treeTypeIconH; @include txtShdwSubtle($shdwItemTreeIcon); - font-size: $d; + font-size: $treeTypeIconH; color: $colorItemTreeIcon; left: $interiorMargin; position: absolute; - @include verticalCenterBlock($menuLineHPx, $d); + @include verticalCenterBlock($menuLineHPx, $treeTypeIconHPx); line-height: 100%; - right: auto; width: $d; + right: auto; width: $treeTypeIconH; .icon { &.l-icon-link, @@ -100,7 +108,8 @@ ul.tree { } } } - .title-label { + .title-label, + .t-title-label { @include absPosDefault(); display: block; left: $runningItemW + ($interiorMargin * 3); @@ -116,7 +125,7 @@ ul.tree { .view-control { color: $colorItemTreeSelectedVC; } - .label .type-icon { + .t-object-label .t-item-icon { color: $colorItemTreeSelectedFg; //$colorItemTreeIconHover; } } @@ -125,9 +134,9 @@ ul.tree { // NOTE: [Mobile] Removed Hover on Mobile @include desktop { &:hover { - background: rgba($colorBodyFg, 0.1); //lighten($colorBodyBg, 5%); - color: pullForward($colorBodyFg, 20%); - .icon { + background: $colorItemTreeHoverBg; + color: $colorItemTreeHoverFg; + .t-item-icon { color: $colorItemTreeIconHover; } } @@ -152,7 +161,7 @@ ul.tree { } .tree-item { - .label { + .t-object-label { left: $interiorMargin + $treeVCW; } } \ No newline at end of file diff --git a/platform/commonUI/general/res/sass/user-environ/_bottom-bar.scss b/platform/commonUI/general/res/sass/user-environ/_bottom-bar.scss deleted file mode 100644 index dd705b0000..0000000000 --- a/platform/commonUI/general/res/sass/user-environ/_bottom-bar.scss +++ /dev/null @@ -1,72 +0,0 @@ -/***************************************************************************** - * Open MCT Web, Copyright (c) 2014-2015, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT Web 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 Web 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. - *****************************************************************************/ -.ue-bottom-bar { - background: $colorFooterBg; - color: lighten($colorBodyBg, 30%); - font-size: .7rem; - //line-height: $ueFooterH - 4px; - //line-height: $ueFooterH; // New status bar design - .status-holder { - //@include border-radius($basicCr * 1.75); // New status bar design - @include box-sizing(border-box); - //background: $colorFooterBg; - //border-bottom: 1px solid lighten($colorBodyBg, 10%); // New status bar design - @include absPosDefault($interiorMargin); - @include ellipsize(); - line-height: $ueFooterH - ($interiorMargin * 2); - right: 120px; - text-transform: uppercase; - } - .app-logo { - @include box-sizing(border-box); - @include absPosDefault($interiorMargin); - left: auto; - cursor: pointer; - //font-size: 0.8em; - //line-height: $ueFooterH - 10px; - //padding-top: 1px; - //text-transform: uppercase; - &.logo-openmctweb { - background: url($dirImgs + 'logo-openmctweb.svg') no-repeat center center; - } - } -} - -.status.block { - //display: inline-block; - display: inline; // New status bar design. Inline to support ellipsis overflow - margin-right: $interiorMarginLg; - .status-indicator { - //@include border-radius($controlCr * 0.9); - //@include box-shadow(inset rgba(black, 0.5) 0 0 3px); - //@include text-shadow(rgba(black, 0.3) 0 0 2px); - display: inline-block; - margin-right: $interiorMarginSm; - color: $colorKey; - &.ok { - color: #009900; - } - &.caution { - color: #ffaa00; - } - } -} \ No newline at end of file diff --git a/platform/commonUI/general/res/sass/user-environ/_layout.scss b/platform/commonUI/general/res/sass/user-environ/_layout.scss index 8e982ef93d..4800cdeb6d 100644 --- a/platform/commonUI/general/res/sass/user-environ/_layout.scss +++ b/platform/commonUI/general/res/sass/user-environ/_layout.scss @@ -29,13 +29,13 @@ } } -.holder-all { +/*.holder-all { $myM: 0; // $interiorMarginSm; top: $myM; right: $myM; bottom: $myM; left: $myM; -} +}*/ .browse-area, .edit-area, @@ -96,12 +96,8 @@ .user-environ { .browse-area, - .edit-area, .editor { - top: $bodyMargin + $ueTopBarH + ($interiorMargin); - right: $bodyMargin; - bottom: $ueFooterH + $bodyMargin; - left: $bodyMargin; + top: 0; left: 0; right: 0; bottom: $ueFooterH; } .browse-area, @@ -115,31 +111,51 @@ .edit-area { $tbH: $btnToolbarH + $interiorMargin; top: $bodyMargin + $ueTopBarEditH + ($interiorMargin); + left: $bodyMargin; + right: $bodyMargin; + bottom: $bodyMargin + $ueFooterH; .tool-bar { bottom: auto; height: $tbH; line-height: $btnToolbarH; } - .work-area { + .object-holder.work-area { top: $tbH + $interiorMargin * 2; + overflow: auto; } } - .ue-bottom-bar { - //@include absPosDefault($bodyMargin); - @include absPosDefault(0); // New status bar design - top: auto; - height: $ueFooterH; - .status-holder { - //right: $ueAppLogoW + $bodyMargin; New status bar design - z-index: 1; - } - .app-logo { - left: auto; - width: $ueAppLogoW; - z-index: 2; - } - } + // from _bottom-bar.scss + .ue-bottom-bar { + @include absPosDefault(0);// New status bar design + top: auto; + height: $ueFooterH; + line-height: $ueFooterH - ($interiorMargin * 2); + background: $colorFooterBg; + color: lighten($colorBodyBg, 30%); + font-size: .7rem; + + .status-holder { + @include box-sizing(border-box); + @include absPosDefault($interiorMargin); + @include ellipsize(); + //line-height: $ueFooterH - ($interiorMargin * 2); + right: 120px; + text-transform: uppercase; + z-index: 1; + } + .app-logo { + @include box-sizing(border-box); + @include absPosDefault($interiorMargin); + cursor: pointer; + left: auto; + width: $ueAppLogoW; + z-index: 2; + &.logo-openmctweb { + background: url($dirImgs + 'logo-openmctweb.svg') no-repeat center center; + } + } + } } .cols { @@ -205,10 +221,19 @@ .browse-mode { .split-layout { - .split-pane-component.pane.left { - min-width: 150px; - max-width: 800px; - width: $ueBrowseLeftPaneW; + .split-pane-component.pane { + //@include test(green); + &.treeview.left { + min-width: 150px; + max-width: 800px; + width: $ueBrowseLeftPaneTreeW; + } + &.t-inspect.right { + min-width: 200px; + max-width: 600px; + //padding-left: $ueCollapsedPaneEdgeM; // Allow room for mini-tab element + width: $ueBrowseRightPaneInspectW; + } } } } @@ -226,16 +251,33 @@ } .pane { + @include box-sizing(border-box); position: absolute; + + .pane-header { + text-transform: uppercase; + height: $ueTopBarH; + line-height: $ueTopBarH; + margin-bottom: $interiorMargin; + } + + .primary-pane { + // Need to lift up this pane to ensure that 'collapsed' panes don't block user interactions + z-index: 2; + } + &.treeview.left { - .create-btn-holder { - bottom: auto; - top: 0; - height: $ueTopBarH; - .wrapper.menu-element { - position: absolute; - bottom: $interiorMargin; - } + //.create-btn-holder { + // //bottom: auto; + // //top: 0; + // height: $ueTopBarH; + // .wrapper.menu-element { + // position: absolute; + // bottom: $interiorMargin; + // } + //} + .holder-create-and-search{ + } .search-holder { top: $ueTopBarH + $interiorMarginLg; @@ -245,6 +287,54 @@ top: $ueTopBarH + $interiorMarginLg + $treeSearchInputBarH + $interiorMargin; } } + + .mini-tab-icon.toggle-pane { + //@include test(blue, 0.3); + z-index: 5; + @include desktop { + $d: $uePaneMiniTabH; + $paneExpandedOffset: $splitterD + $uePaneMiniTabW; + top: $bodyMargin; + height: $d; + line-height: $d; + &:after { + // Always the icon that shows when the pane is collapsed + opacity: 0; + } + &.collapsed { + &:before { + opacity: 0; + } + &:after { + opacity: 1; + } + } + &.toggle-tree.anchor-left { + left: 0; + @include transform(translateX(-1 * $paneExpandedOffset)); + &:after { + content: '\6d'; // Menu 'hamburger' icon + } + &.collapsed { + left: 0; + @include transform(translateX((-1 * $ueCollapsedPaneEdgeM) + $interiorMargin)); + } + &:not(.collapsed):before { + @include trans-prop-nice(opacity, 200ms, 200ms); + } + } + &.toggle-inspect.anchor-right { + right: $bodyMargin; + &:after { + content: '\e615'; // e615: Crosshair icon; was e608: Info "i" icon + } + &.collapsed { + right: $interiorMargin; + } + } + } + } + &.items { .object-browse-bar { .left.abs, @@ -266,23 +356,44 @@ } } } - &.vertical { + /* &.vertical { // Slides left and right - > .pane { - // @include test(); - margin-left: $interiorMargin; + > .pane.left { > .holder { - left: 0; - right: 0; - } - &:first-child { - margin-left: 0; - .holder { - right: $interiorMarginSm; - } + left: $bodyMargin; } } + > .pane.right { + > .holder { + right: $bodyMargin; + } + } + }*/ + // Specific elements margins + .holder.holder-create-and-search { + top: $bodyMargin; + right: 0; + bottom: $bodyMargin; + left: $bodyMargin; + } + + .holder.holder-object-and-inspector { + top: 0; + right: 0; + bottom: 0; + left: 0; + .holder-object { + top: $bodyMargin; + bottom: $bodyMargin; + } + .holder-inspector-elements { + top: $bodyMargin; + bottom: $bodyMargin; + left: $bodyMargin; + right: $bodyMargin; + + } } } @@ -340,4 +451,84 @@ @include webkitProp(flex, '1 1 0'); padding-right: $interiorMarginLg; } -} \ No newline at end of file +} + +// When the tree is hidden, these are the +// classes used for the left menu and the +// right representation. +.pane-tree-hidden { + // Sets the left tree menu when the tree is hidden. + //.pane.left.treeview, + .tree-holder, + .splitter-treeview, + .holder-create-and-search { + opacity: 0; + } + /*.holder-create-and-search { + @include trans-prop-nice((top, left), 250ms); + top: $ueTopBarH + $interiorMargin; + left: -1 * $bodyMargin !important; + .create-btn { + @include border-left-radius(0); + @include trans-prop-nice((width), 250ms); + width: $uePaneMiniTabW !important; + text-align: center !important; + padding: 0; + .title-label, + &:after { + display: none; + } + &:before { + font-size: 9px; + } + } + }*/ +} + +.pane-tree-showing { + // Sets the left tree menu when the tree is shown. + //.pane.left.treeview, + .tree-holder, + .splitter-treeview { + @include trans-prop-nice(opacity, $dur: 250ms, $delay: 250ms); + opacity: 1; + } + + .holder-create-and-search { + @include trans-prop-nice(opacity, $dur: 250ms, $delay: 200ms); + } +} + +.pane-inspect-showing { + .l-object-and-inspector { + .l-inspect, + .splitter-inspect { + @include trans-prop-nice(opacity, $dur: 250ms, $delay: 250ms); + opacity: 1; + } + } +} +.pane-inspect-hidden { + .l-object-and-inspector { + .l-inspect, + .splitter-inspect { + opacity: 0; + } + } +} + +@include desktop { + .pane.treeview.left .tree-holder { + padding-right: $interiorMargin; + } + .pane-tree-hidden { + .pane.right.primary-pane { left: $ueCollapsedPaneEdgeM !important; } + } + .pane-inspect-hidden .l-object-and-inspector { + .pane.left { right: $ueCollapsedPaneEdgeM !important; } + } + + .pane:not(.resizing) { + @include trans-prop-nice-resize-w(250ms); + } +} diff --git a/platform/commonUI/general/res/templates/bottombar.html b/platform/commonUI/general/res/templates/bottombar.html index 4da2686fa1..f0d3799542 100644 --- a/platform/commonUI/general/res/templates/bottombar.html +++ b/platform/commonUI/general/res/templates/bottombar.html @@ -26,5 +26,6 @@ key="indicator.template">
+
\ No newline at end of file diff --git a/platform/commonUI/general/res/templates/controls/datetime-field.html b/platform/commonUI/general/res/templates/controls/datetime-field.html new file mode 100644 index 0000000000..6ba8cbf901 --- /dev/null +++ b/platform/commonUI/general/res/templates/controls/datetime-field.html @@ -0,0 +1,20 @@ + + + + + + +
+ + +
+
+
diff --git a/platform/commonUI/general/res/templates/controls/time-controller.html b/platform/commonUI/general/res/templates/controls/time-controller.html index 300e56c381..e44a9ff77c 100644 --- a/platform/commonUI/general/res/templates/controls/time-controller.html +++ b/platform/commonUI/general/res/templates/controls/time-controller.html @@ -22,47 +22,24 @@
C - - - - - - - -
- - -
-
-
+ + + to - - - - - - - -
- - -
-
-
  + +  
@@ -97,7 +74,7 @@
diff --git a/platform/commonUI/general/res/templates/indicator.html b/platform/commonUI/general/res/templates/indicator.html index 34ea2fe9c8..e9be598b18 100644 --- a/platform/commonUI/general/res/templates/indicator.html +++ b/platform/commonUI/general/res/templates/indicator.html @@ -19,20 +19,21 @@ this source code distribution or the Licensing information page available at runtime from the About dialog for additional information. --> + +
- - {{ngModel.getGlyph()}} - - - {{ngModel.getText()}} - - + + {{ngModel.getGlyph()}} + + {{ngModel.getText()}} + + + G diff --git a/platform/commonUI/general/res/templates/label.html b/platform/commonUI/general/res/templates/label.html index 7ca73bb026..beb57626a5 100644 --- a/platform/commonUI/general/res/templates/label.html +++ b/platform/commonUI/general/res/templates/label.html @@ -19,16 +19,7 @@ this source code distribution or the Licensing information page available at runtime from the About dialog for additional information. --> - - - {{type.getGlyph()}} - - - - - {{model.name}} - + +{{type.getGlyph()}} +{{model.name}} diff --git a/platform/commonUI/general/res/templates/message-banner.html b/platform/commonUI/general/res/templates/message-banner.html new file mode 100644 index 0000000000..44c7f915b6 --- /dev/null +++ b/platform/commonUI/general/res/templates/message-banner.html @@ -0,0 +1,21 @@ + \ No newline at end of file diff --git a/platform/commonUI/general/res/templates/object-inspector.html b/platform/commonUI/general/res/templates/object-inspector.html new file mode 100644 index 0000000000..83ed591ff7 --- /dev/null +++ b/platform/commonUI/general/res/templates/object-inspector.html @@ -0,0 +1,63 @@ + + +
+
Inspection
+
    +
  • + Properties +
    +
    {{ data.name }}
    +
    {{ data.value }}
    +
    +
  • +
  • + Location + + + + +
  • +
  • + Original Location + + + + +
  • +
+
+
diff --git a/platform/commonUI/general/res/templates/progress-bar.html b/platform/commonUI/general/res/templates/progress-bar.html new file mode 100644 index 0000000000..a24a4fab29 --- /dev/null +++ b/platform/commonUI/general/res/templates/progress-bar.html @@ -0,0 +1,10 @@ + + + + + +
+ {{ngModel.progress}}% complete. + {{ngModel.progressText}} +
\ No newline at end of file diff --git a/platform/commonUI/general/src/controllers/BannerController.js b/platform/commonUI/general/src/controllers/BannerController.js new file mode 100644 index 0000000000..4be9304cc6 --- /dev/null +++ b/platform/commonUI/general/src/controllers/BannerController.js @@ -0,0 +1,68 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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 define*/ + +define( + [], + function () { + "use strict"; + + /** + * A controller for banner notifications. Banner notifications are a + * non-blocking way of drawing the user's attention to an event such + * as system errors, or the progress or successful completion of an + * ongoing task. This controller provides scoped functions for + * dismissing and 'maximizing' notifications. See {@link NotificationService} + * for more details on Notifications. + * + * @param $scope + * @param notificationService + * @param dialogService + * @constructor + */ + function BannerController($scope, notificationService, dialogService) { + $scope.active = notificationService.active; + + $scope.action = function (action, $event){ + /* + Prevents default 'maximize' behaviour when clicking on + notification button + */ + $event.stopPropagation(); + return action(); + }; + $scope.dismiss = function(notification, $event) { + $event.stopPropagation(); + notification.dismissOrMinimize(); + }; + $scope.maximize = function(notification) { + if (notification.model.severity !== "info"){ + + notification.model.cancel = function(){ + dialogService.dismiss(); + }; + dialogService.showBlockingMessage(notification.model); + } + }; + } + return BannerController; + }); \ No newline at end of file diff --git a/platform/commonUI/general/src/controllers/DateTimeFieldController.js b/platform/commonUI/general/src/controllers/DateTimeFieldController.js new file mode 100644 index 0000000000..b87268dc5a --- /dev/null +++ b/platform/commonUI/general/src/controllers/DateTimeFieldController.js @@ -0,0 +1,79 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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 define,Promise*/ + +define( + [], + function () { + 'use strict'; + + /** + * Controller to support the date-time entry field. + * + * Accepts a `format` property in the `structure` attribute + * which allows a date/time to be specified via its symbolic + * key (as will be used to look up said format from the + * `formatService`.) + * + * {@see FormatService} + * @constructor + * @memberof platform/commonUI/general + * @param $scope the Angular scope for this controller + * @param {FormatService} formatService the service to user to format + * domain values + * @param {string} defaultFormat the format to request when no + * format has been otherwise specified + */ + function DateTimeFieldController($scope, formatService, defaultFormat) { + var formatter = formatService.getFormat(defaultFormat); + + function updateFromModel(value) { + // Only reformat if the value is different from user + // input (to avoid reformatting valid input while typing.) + if (!formatter.validate($scope.textValue) || + formatter.parse($scope.textValue) !== value) { + $scope.textValue = formatter.format(value); + $scope.textInvalid = false; + } + } + + function updateFromView(textValue) { + $scope.textInvalid = !formatter.validate(textValue); + if (!$scope.textInvalid) { + $scope.ngModel[$scope.field] = + formatter.parse(textValue); + } + } + + function setFormat(format) { + formatter = formatService.getFormat(format || defaultFormat); + updateFromModel($scope.ngModel[$scope.field]); + } + + $scope.$watch('structure.format', setFormat); + $scope.$watch('ngModel[field]', updateFromModel); + $scope.$watch('textValue', updateFromView); + } + + return DateTimeFieldController; + } +); diff --git a/platform/commonUI/general/src/controllers/ObjectInspectorController.js b/platform/commonUI/general/src/controllers/ObjectInspectorController.js new file mode 100644 index 0000000000..5b04304af0 --- /dev/null +++ b/platform/commonUI/general/src/controllers/ObjectInspectorController.js @@ -0,0 +1,117 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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 define,Promise*/ + +/** + * Module defining ObjectInspectorController. Created by shale on 08/21/2015. + */ +define( + [], + function () { + "use strict"; + + /** + * The ObjectInspectorController gets and formats the data for + * the inspector display + * + * @constructor + */ + function ObjectInspectorController($scope, objectService) { + $scope.primaryParents = []; + $scope.contextutalParents = []; + //$scope.isLink = false; + + // Gets an array of the contextual parents/anscestors of the selected object + function getContextualPath() { + var currentObj = $scope.ngModel.selectedObject, + currentParent, + parents = []; + + currentParent = currentObj && + currentObj.hasCapability('context') && + currentObj.getCapability('context').getParent(); + + while (currentParent && currentParent.getModel().type !== 'root' && + currentParent.hasCapability('context')) { + // Record this object + parents.unshift(currentParent); + + // Get the next one up the tree + currentObj = currentParent; + currentParent = currentObj.getCapability('context').getParent(); + } + + $scope.contextutalParents = parents; + } + + // Gets an array of the parents/anscestors of the selected object's + // primary location (locational of original non-link) + function getPrimaryPath(current) { + var location; + + // If this the the initial call of this recursive function + if (!current) { + current = $scope.ngModel.selectedObject; + $scope.primaryParents = []; + } + + location = current.getModel().location; + + if (location && location !== 'root') { + objectService.getObjects([location]).then(function (obj) { + var next = obj[location]; + + $scope.primaryParents.unshift(next); + getPrimaryPath(next); + }); + } + + } + + // Gets the metadata for the selected object + function getMetadata() { + $scope.metadata = $scope.ngModel.selectedObject && + $scope.ngModel.selectedObject.hasCapability('metadata') && + $scope.ngModel.selectedObject.useCapability('metadata'); + } + + // Set scope variables when the selected object changes + $scope.$watch('ngModel.selectedObject', function () { + $scope.isLink = $scope.ngModel.selectedObject && + $scope.ngModel.selectedObject.hasCapability('location') && + $scope.ngModel.selectedObject.getCapability('location').isLink(); + + if ($scope.isLink) { + getPrimaryPath(); + getContextualPath(); + } else { + $scope.primaryParents = []; + getContextualPath(); + } + + getMetadata(); + }); + } + + return ObjectInspectorController; + } +); \ No newline at end of file diff --git a/platform/commonUI/general/src/controllers/TimeRangeController.js b/platform/commonUI/general/src/controllers/TimeRangeController.js index d4fb21be08..cdcdb7f8d0 100644 --- a/platform/commonUI/general/src/controllers/TimeRangeController.js +++ b/platform/commonUI/general/src/controllers/TimeRangeController.js @@ -26,33 +26,32 @@ define( function (moment) { "use strict"; - var DATE_FORMAT = "YYYY-MM-DD HH:mm:ss", - TICK_SPACING_PX = 150; + var TICK_SPACING_PX = 150; + /** + * Controller used by the `time-controller` template. * @memberof platform/commonUI/general * @constructor + * @param $scope the Angular scope for this controller + * @param {FormatService} formatService the service to user to format + * domain values + * @param {string} defaultFormat the format to request when no + * format has been otherwise specified + * @param {Function} now a function to return current system time */ - function TimeConductorController($scope, now) { + function TimeRangeController($scope, formatService, defaultFormat, now) { var tickCount = 2, innerMinimumSpan = 1000, // 1 second outerMinimumSpan = 1000 * 60 * 60, // 1 hour - initialDragValue; + initialDragValue, + formatter = formatService.getFormat(defaultFormat); function formatTimestamp(ts) { - return moment.utc(ts).format(DATE_FORMAT); + return formatter.format(ts); } - function parseTimestamp(text) { - var m = moment.utc(text, DATE_FORMAT); - if (m.isValid()) { - return m.valueOf(); - } else { - throw new Error("Could not parse " + text); - } - } - - // From 0.0-1.0 to "0%"-"1%" + // From 0.0-1.0 to "0%"-"100%" function toPercent(p) { return (100 * p) + "%"; } @@ -101,41 +100,15 @@ define( return { start: bounds.start, end: bounds.end }; } - function updateBoundsTextForProperty(ngModel, property) { - try { - if (!$scope.boundsModel[property] || - parseTimestamp($scope.boundsModel[property]) !== - ngModel.outer[property]) { - $scope.boundsModel[property] = - formatTimestamp(ngModel.outer[property]); - } - } catch (e) { - // User-entered text is invalid, so leave it be - // until they fix it. - } - } - - function updateBoundsText(ngModel) { - updateBoundsTextForProperty(ngModel, 'start'); - updateBoundsTextForProperty(ngModel, 'end'); - } - function updateViewFromModel(ngModel) { - var t = now(); - ngModel = ngModel || {}; ngModel.outer = ngModel.outer || defaultBounds(); ngModel.inner = ngModel.inner || copyBounds(ngModel.outer); - // First, dates for the date pickers for outer bounds - updateBoundsText(ngModel); - - // Then various updates for the inner span - updateViewForInnerSpanFromModel(ngModel); - // Stick it back is scope (in case we just set defaults) $scope.ngModel = ngModel; + updateViewForInnerSpanFromModel(ngModel); updateTicks(); } @@ -155,7 +128,8 @@ define( } function toMillis(pixels) { - var span = $scope.ngModel.outer.end - $scope.ngModel.outer.start; + var span = + $scope.ngModel.outer.end - $scope.ngModel.outer.start; return (pixels / $scope.spanWidth) * span; } @@ -243,36 +217,10 @@ define( updateTicks(); } - function updateStartFromText(value) { - try { - updateOuterStart(parseTimestamp(value)); - updateBoundsTextForProperty($scope.ngModel, 'end'); - $scope.boundsModel.startValid = true; - } catch (e) { - $scope.boundsModel.startValid = false; - return; - } - } - - function updateEndFromText(value) { - try { - updateOuterEnd(parseTimestamp(value)); - updateBoundsTextForProperty($scope.ngModel, 'start'); - $scope.boundsModel.endValid = true; - } catch (e) { - $scope.boundsModel.endValid = false; - return; - } - } - - function updateStartFromPicker(value) { - updateOuterStart(value); - updateBoundsText($scope.ngModel); - } - - function updateEndFromPicker(value) { - updateOuterEnd(value); - updateBoundsText($scope.ngModel); + function updateFormat(key) { + formatter = formatService.getFormat(key || defaultFormat); + updateViewForInnerSpanFromModel($scope.ngModel); + updateTicks(); } $scope.startLeftDrag = startLeftDrag; @@ -282,21 +230,18 @@ define( $scope.rightDrag = rightDrag; $scope.middleDrag = middleDrag; - $scope.state = false; $scope.ticks = []; - $scope.boundsModel = {}; // Initialize scope to defaults updateViewFromModel($scope.ngModel); $scope.$watchCollection("ngModel", updateViewFromModel); $scope.$watch("spanWidth", updateSpanWidth); - $scope.$watch("ngModel.outer.start", updateStartFromPicker); - $scope.$watch("ngModel.outer.end", updateEndFromPicker); - $scope.$watch("boundsModel.start", updateStartFromText); - $scope.$watch("boundsModel.end", updateEndFromText); + $scope.$watch("ngModel.outer.start", updateOuterStart); + $scope.$watch("ngModel.outer.end", updateOuterEnd); + $scope.$watch("parameters.format", updateFormat); } - return TimeConductorController; + return TimeRangeController; } ); diff --git a/platform/commonUI/general/src/directives/MCTSplitPane.js b/platform/commonUI/general/src/directives/MCTSplitPane.js index abc54f772e..9abc641ebd 100644 --- a/platform/commonUI/general/src/directives/MCTSplitPane.js +++ b/platform/commonUI/general/src/directives/MCTSplitPane.js @@ -132,10 +132,10 @@ define( // Get actual size (to obey min-width etc.) firstSize = getSize(first[0]); first.css(anchor.dimension, firstSize + 'px'); - splitter.css(anchor.edge, (firstSize + splitterSize) + 'px'); + splitter.css(anchor.edge, firstSize + 'px'); splitter.css(anchor.opposite, "auto"); - last.css(anchor.edge, (firstSize + splitterSize * 3) + 'px'); + last.css(anchor.edge, (firstSize + splitterSize) + 'px'); last.css(anchor.opposite, "0px"); position = firstSize + splitterSize; @@ -178,6 +178,12 @@ define( return position; } + // Dynamically apply a CSS class to elements when the user + // is actively resizing + function toggleClass(classToToggle) { + $element.children().toggleClass(classToToggle); + } + // Make sure anchor parameter is something we know if (!ANCHORS[anchorKey]) { $log.warn(ANCHOR_WARNING_MESSAGE); @@ -208,6 +214,7 @@ define( // Interface exposed by controller, for mct-splitter to user return { position: getSetPosition, + toggleClass: toggleClass, anchor: function () { return anchor; } diff --git a/platform/commonUI/general/src/directives/MCTSplitter.js b/platform/commonUI/general/src/directives/MCTSplitter.js index c163c107e0..ad8f809c65 100644 --- a/platform/commonUI/general/src/directives/MCTSplitter.js +++ b/platform/commonUI/general/src/directives/MCTSplitter.js @@ -29,7 +29,8 @@ define( // Pixel width to allocate for the splitter itself var SPLITTER_TEMPLATE = "
", + "mct-drag=\"splitter.move(delta)\" " + + "mct-drag-up=\"splitter.endMove()\">
", OFFSETS_BY_EDGE = { left: "offsetLeft", right: "offsetRight", @@ -53,6 +54,7 @@ define( startMove: function () { var splitter = element[0]; initialPosition = mctSplitPane.position(); + mctSplitPane.toggleClass('resizing'); }, // Handle user changes to splitter position move: function (delta) { @@ -63,6 +65,11 @@ define( // Update the position of this splitter mctSplitPane.position(initialPosition + pixelDelta); + }, + // Grab the event when the user is done moving + // the splitter and pass it on + endMove: function() { + mctSplitPane.toggleClass('resizing'); } }; } diff --git a/platform/commonUI/general/test/controllers/DateTimeFieldControllerSpec.js b/platform/commonUI/general/test/controllers/DateTimeFieldControllerSpec.js new file mode 100644 index 0000000000..8f516ece5d --- /dev/null +++ b/platform/commonUI/general/test/controllers/DateTimeFieldControllerSpec.js @@ -0,0 +1,183 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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 define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/ + +define( + ["../../src/controllers/DateTimeFieldController", "moment"], + function (DateTimeFieldController, moment) { + 'use strict'; + + var TEST_FORMAT = "YYYY-MM-DD HH:mm:ss"; + + describe("The DateTimeFieldController", function () { + var mockScope, + mockFormatService, + mockFormat, + controller; + + function fireWatch(expr, value) { + mockScope.$watch.calls.forEach(function (call) { + if (call.args[0] === expr) { + call.args[1](value); + } + }); + } + + beforeEach(function () { + mockScope = jasmine.createSpyObj('$scope', ['$watch']); + mockFormatService = + jasmine.createSpyObj('formatService', ['getFormat']); + mockFormat = jasmine.createSpyObj('format', [ + 'parse', + 'validate', + 'format' + ]); + + mockFormatService.getFormat.andReturn(mockFormat); + + mockFormat.validate.andCallFake(function (text) { + return moment.utc(text, TEST_FORMAT).isValid(); + }); + mockFormat.parse.andCallFake(function (text) { + return moment.utc(text, TEST_FORMAT).valueOf(); + }); + mockFormat.format.andCallFake(function (value) { + return moment.utc(value).format(TEST_FORMAT); + }); + + mockScope.ngModel = { testField: 12321 }; + mockScope.field = "testField"; + mockScope.structure = { format: "someFormat" }; + + controller = new DateTimeFieldController( + mockScope, + mockFormatService + ); + }); + + it("updates models from user-entered text", function () { + var newText = "1977-05-25 17:30:00"; + + mockScope.textValue = newText; + fireWatch("textValue", newText); + expect(mockScope.ngModel.testField) + .toEqual(mockFormat.parse(newText)); + expect(mockScope.textInvalid).toBeFalsy(); + }); + + it("updates text from model values", function () { + var testTime = mockFormat.parse("1977-05-25 17:30:00"); + mockScope.ngModel.testField = testTime; + fireWatch("ngModel[field]", testTime); + expect(mockScope.textValue).toEqual("1977-05-25 17:30:00"); + }); + + describe("when user input is invalid", function () { + var newText, oldValue; + + beforeEach(function () { + newText = "Not a date"; + oldValue = mockScope.ngModel.testField; + mockScope.textValue = newText; + fireWatch("textValue", newText); + }); + + it("displays error state", function () { + expect(mockScope.textInvalid).toBeTruthy(); + }); + + it("does not modify model state", function () { + expect(mockScope.ngModel.testField).toEqual(oldValue); + }); + + it("does not modify user input", function () { + expect(mockScope.textValue).toEqual(newText); + }); + }); + + it("does not modify valid but irregular user input", function () { + // Don't want the controller "fixing" bad or + // irregularly-formatted input out from under + // the user's fingertips. + var newText = "2015-3-3 01:02:04", + oldValue = mockScope.ngModel.testField; + + mockFormat.validate.andReturn(true); + mockFormat.parse.andReturn(42); + mockScope.textValue = newText; + fireWatch("textValue", newText); + + expect(mockScope.textValue).toEqual(newText); + expect(mockScope.ngModel.testField).toEqual(42); + expect(mockScope.ngModel.testField).not.toEqual(oldValue); + }); + + it("obtains a format from the format service", function () { + fireWatch('structure.format', mockScope.structure.format); + expect(mockFormatService.getFormat) + .toHaveBeenCalledWith(mockScope.structure.format); + }); + + it("throws an error for unknown formats", function () { + mockFormatService.getFormat.andReturn(undefined); + expect(function () { + fireWatch("structure.format", "some-format"); + }).toThrow(); + }); + + describe("using the obtained format", function () { + var testValue = 1234321, + testText = "some text"; + + beforeEach(function () { + mockFormat.validate.andReturn(true); + mockFormat.parse.andReturn(testValue); + mockFormat.format.andReturn(testText); + }); + + it("parses user input", function () { + var newText = "some other new text"; + mockScope.textValue = newText; + fireWatch("textValue", newText); + expect(mockFormat.parse).toHaveBeenCalledWith(newText); + expect(mockScope.ngModel.testField).toEqual(testValue); + }); + + it("validates user input", function () { + var newText = "some other new text"; + mockScope.textValue = newText; + fireWatch("textValue", newText); + expect(mockFormat.validate).toHaveBeenCalledWith(newText); + }); + + it("formats model data for display", function () { + var newValue = 42; + mockScope.ngModel.testField = newValue; + fireWatch("ngModel[field]", newValue); + expect(mockFormat.format).toHaveBeenCalledWith(newValue); + expect(mockScope.textValue).toEqual(testText); + }); + }); + + }); + } +); diff --git a/platform/commonUI/general/test/controllers/ObjectInspectorControllerSpec.js b/platform/commonUI/general/test/controllers/ObjectInspectorControllerSpec.js new file mode 100644 index 0000000000..496467ea2d --- /dev/null +++ b/platform/commonUI/general/test/controllers/ObjectInspectorControllerSpec.js @@ -0,0 +1,112 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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 define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/ + +/** + * Created by shale on 08/24/2015. + */ +define( + ["../../src/controllers/ObjectInspectorController"], + function (ObjectInspectorController) { + "use strict"; + + describe("The object inspector controller ", function () { + var mockScope, + mockObjectService, + mockPromise, + mockDomainObject, + mockContextCapability, + mockLocationCapability, + controller; + + beforeEach(function () { + mockScope = jasmine.createSpyObj( + "$scope", + [ "$watch" ] + ); + mockScope.ngModel = {}; + mockScope.ngModel.selectedObject = 'mock selected object'; + + mockObjectService = jasmine.createSpyObj( + "objectService", + [ "getObjects" ] + ); + mockPromise = jasmine.createSpyObj( + "promise", + [ "then" ] + ); + mockObjectService.getObjects.andReturn(mockPromise); + + mockDomainObject = jasmine.createSpyObj( + "selectedObject", + [ "hasCapability", "getCapability", "useCapability", "getModel" ] + ); + mockDomainObject.getModel.andReturn({location: 'somewhere'}); + mockDomainObject.hasCapability.andReturn(true); + + mockContextCapability = jasmine.createSpyObj( + "context capability", + [ "getParent" ] + ); + mockLocationCapability = jasmine.createSpyObj( + "location capability", + [ "isLink" ] + ); + mockDomainObject.getCapability.andCallFake(function (param) { + if (param === 'location') { + return mockLocationCapability; + } else if (param === 'context') { + return mockContextCapability; + } + }); + + controller = new ObjectInspectorController(mockScope, mockObjectService); + + // Change the selected object to trigger the watch call + mockScope.ngModel.selectedObject = mockDomainObject; + }); + + it("watches for changes to the selected object", function () { + expect(mockScope.$watch).toHaveBeenCalledWith('ngModel.selectedObject', jasmine.any(Function)); + }); + + it("looks for contextual parent objects", function () { + mockScope.$watch.mostRecentCall.args[1](); + expect(mockContextCapability.getParent).toHaveBeenCalled(); + }); + + it("if link, looks for primary parent objects", function () { + mockLocationCapability.isLink.andReturn(true); + + mockScope.$watch.mostRecentCall.args[1](); + expect(mockDomainObject.getModel).toHaveBeenCalled(); + expect(mockObjectService.getObjects).toHaveBeenCalled(); + mockPromise.then.mostRecentCall.args[0]({'somewhere': mockDomainObject}); + }); + + it("gets metadata", function () { + mockScope.$watch.mostRecentCall.args[1](); + expect(mockDomainObject.useCapability).toHaveBeenCalledWith('metadata'); + }); + }); + } +); \ No newline at end of file diff --git a/platform/commonUI/general/test/controllers/TimeRangeControllerSpec.js b/platform/commonUI/general/test/controllers/TimeRangeControllerSpec.js index 91d3ecb9db..85e77e4889 100644 --- a/platform/commonUI/general/test/controllers/TimeRangeControllerSpec.js +++ b/platform/commonUI/general/test/controllers/TimeRangeControllerSpec.js @@ -33,7 +33,10 @@ define( describe("The TimeRangeController", function () { var mockScope, + mockFormatService, + testDefaultFormat, mockNow, + mockFormat, controller; function fireWatch(expr, value) { @@ -57,8 +60,30 @@ define( "$scope", [ "$apply", "$watch", "$watchCollection" ] ); + mockFormatService = jasmine.createSpyObj( + "formatService", + [ "getFormat" ] + ); + testDefaultFormat = 'utc'; + mockFormat = jasmine.createSpyObj( + "format", + [ "validate", "format", "parse" ] + ); + + mockFormatService.getFormat.andReturn(mockFormat); + + mockFormat.format.andCallFake(function (value) { + return moment.utc(value).format("YYYY-MM-DD HH:mm:ss"); + }); + mockNow = jasmine.createSpy('now'); - controller = new TimeRangeController(mockScope, mockNow); + + controller = new TimeRangeController( + mockScope, + mockFormatService, + testDefaultFormat, + mockNow + ); }); it("watches the model that was passed in", function () { @@ -167,70 +192,22 @@ define( .toBeGreaterThan(mockScope.ngModel.inner.start); }); - describe("by typing", function () { - it("updates models", function () { - var newStart = "1977-05-25 17:30:00", - newEnd = "2015-12-18 03:30:00"; - - mockScope.boundsModel.start = newStart; - fireWatch("boundsModel.start", newStart); - expect(mockScope.ngModel.outer.start) - .toEqual(moment.utc(newStart).valueOf()); - expect(mockScope.boundsModel.startValid) - .toBeTruthy(); - - mockScope.boundsModel.end = newEnd; - fireWatch("boundsModel.end", newEnd); - expect(mockScope.ngModel.outer.end) - .toEqual(moment.utc(newEnd).valueOf()); - expect(mockScope.boundsModel.endValid) - .toBeTruthy(); - }); - - it("displays error state", function () { - var newStart = "Not a date", - newEnd = "Definitely not a date", - oldStart = mockScope.ngModel.outer.start, - oldEnd = mockScope.ngModel.outer.end; - - mockScope.boundsModel.start = newStart; - fireWatch("boundsModel.start", newStart); - expect(mockScope.ngModel.outer.start) - .toEqual(oldStart); - expect(mockScope.boundsModel.startValid) - .toBeFalsy(); - - mockScope.boundsModel.end = newEnd; - fireWatch("boundsModel.end", newEnd); - expect(mockScope.ngModel.outer.end) - .toEqual(oldEnd); - expect(mockScope.boundsModel.endValid) - .toBeFalsy(); - }); - - it("does not modify user input", function () { - // Don't want the controller "fixing" bad or - // irregularly-formatted input out from under - // the user's fingertips. - var newStart = "Not a date", - newEnd = "2015-3-3 01:02:04", - oldStart = mockScope.ngModel.outer.start, - oldEnd = mockScope.ngModel.outer.end; - - mockScope.boundsModel.start = newStart; - fireWatch("boundsModel.start", newStart); - expect(mockScope.boundsModel.start) - .toEqual(newStart); - - mockScope.boundsModel.end = newEnd; - fireWatch("boundsModel.end", newEnd); - expect(mockScope.boundsModel.end) - .toEqual(newEnd); - }); - }); }); + it("watches for changes in format selection", function () { + expect(mockFormatService.getFormat) + .not.toHaveBeenCalledWith('test-format'); + fireWatch("parameters.format", 'test-format'); + expect(mockFormatService.getFormat) + .toHaveBeenCalledWith('test-format'); + }); + it("throws an error for unknown formats", function () { + mockFormatService.getFormat.andReturn(undefined); + expect(function () { + fireWatch("parameters.format", "some-format"); + }).toThrow(); + }); }); } diff --git a/platform/commonUI/general/test/suite.json b/platform/commonUI/general/test/suite.json index 0d19fbb9e4..6b89f83d61 100644 --- a/platform/commonUI/general/test/suite.json +++ b/platform/commonUI/general/test/suite.json @@ -3,8 +3,10 @@ "controllers/BottomBarController", "controllers/ClickAwayController", "controllers/ContextMenuController", + "controllers/DateTimeFieldController", "controllers/DateTimePickerController", "controllers/GetterSetterController", + "controllers/ObjectInspectorController", "controllers/SelectorController", "controllers/SplitPaneController", "controllers/TimeRangeController", diff --git a/platform/commonUI/notification/bundle.json b/platform/commonUI/notification/bundle.json new file mode 100644 index 0000000000..4851dd28b6 --- /dev/null +++ b/platform/commonUI/notification/bundle.json @@ -0,0 +1,45 @@ +{ + "extensions": { + "constants": [ + { + "key": "DEFAULT_AUTO_DISMISS", + "value": 3000 + }, + { + "key": "FORCE_AUTO_DISMISS", + "value": 1000 + }, + { + "key": "MINIMIZE_TIMEOUT", + "value": 300 + } + ], + "templates": [ + { + "key":"notificationIndicatorTemplate", + "templateUrl": "notification-indicator.html" + } + ], + "controllers": [ + { + "key": "NotificationIndicatorController", + "implementation": "NotificationIndicatorController.js", + "depends": ["$scope", "notificationService", "dialogService"] + } + ], + "indicators": [ + { + "implementation": "NotificationIndicator.js", + "priority": "fallback" + } + ], + "services": [ + { + "key": "notificationService", + "implementation": "NotificationService.js", + "depends": [ "$timeout", "DEFAULT_AUTO_DISMISS", + "MINIMIZE_TIMEOUT" ] + } + ] + } +} diff --git a/platform/commonUI/notification/res/notification-indicator.html b/platform/commonUI/notification/res/notification-indicator.html new file mode 100644 index 0000000000..9c7e80a639 --- /dev/null +++ b/platform/commonUI/notification/res/notification-indicator.html @@ -0,0 +1,10 @@ + + + + {{notifications.length}} + Notifications + + {{notifications.length}} + \ No newline at end of file diff --git a/platform/commonUI/general/res/sass/_properties.scss b/platform/commonUI/notification/src/NotificationIndicator.js similarity index 81% rename from platform/commonUI/general/res/sass/_properties.scss rename to platform/commonUI/notification/src/NotificationIndicator.js index b48fe66406..29a831d251 100644 --- a/platform/commonUI/general/res/sass/_properties.scss +++ b/platform/commonUI/notification/src/NotificationIndicator.js @@ -19,17 +19,17 @@ * this source code distribution or the Licensing information page available * at runtime from the About dialog for additional information. *****************************************************************************/ -/* Classes to be used for lists of properties and values */ +/*global define,window*/ -.properties { - .s-row { - border-top: 1px solid $colorInteriorBorder; - font-size: 0.8em; - &:first-child { - border: none; - } - .s-value { - color: #fff; - } +define( + [], + function () { + "use strict"; + + function NotificationIndicator() {} + + NotificationIndicator.template = 'notificationIndicatorTemplate'; + + return NotificationIndicator; } -} \ No newline at end of file +); diff --git a/platform/commonUI/notification/src/NotificationIndicatorController.js b/platform/commonUI/notification/src/NotificationIndicatorController.js new file mode 100644 index 0000000000..e58b0a606b --- /dev/null +++ b/platform/commonUI/notification/src/NotificationIndicatorController.js @@ -0,0 +1,67 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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 define*/ + +define( + [], + function () { + "use strict"; + + /** + * Provides an indicator that is visible when there are + * banner notifications that have been minimized. Will also indicate + * the number of notifications. Notifications can be viewed by + * clicking on the indicator to launch a dialog showing a list of + * notifications. + * @param $scope + * @param notificationService + * @param dialogService + * @constructor + */ + function NotificationIndicatorController($scope, notificationService, dialogService) { + $scope.notifications = notificationService.notifications; + $scope.highest = notificationService.highest; + + /** + * Launch a dialog showing a list of current notifications. + */ + $scope.showNotificationsList = function(){ + dialogService.getDialogResponse('overlay-message-list', { + dialog: { + title: "Messages", + //Launch the message list dialog with the models + // from the notifications + messages: notificationService.notifications && notificationService.notifications.map(function(notification){ + return notification.model; + }) + }, + cancel: function(){ + dialogService.dismiss(); + } + }); + + }; + } + return NotificationIndicatorController; + } +); + diff --git a/platform/commonUI/notification/src/NotificationService.js b/platform/commonUI/notification/src/NotificationService.js new file mode 100644 index 0000000000..568d31beb5 --- /dev/null +++ b/platform/commonUI/notification/src/NotificationService.js @@ -0,0 +1,387 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web 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 Web 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 define*/ + +/** + * 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( + [], + function () { + "use strict"; + + /** + * 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
@@ -71,7 +71,7 @@  - Filtered by: {{ ngModel.filtersString }} + Filtered by: {{ ngModel.filtersString }}
diff --git a/platform/telemetry/bundle.json b/platform/telemetry/bundle.json index 69b32b54c7..d264a1d6c0 100644 --- a/platform/telemetry/bundle.json +++ b/platform/telemetry/bundle.json @@ -37,7 +37,8 @@ "services": [ { "key": "telemetryFormatter", - "implementation": "TelemetryFormatter.js" + "implementation": "TelemetryFormatter.js", + "depends": [ "formatService", "DEFAULT_TIME_FORMAT" ] }, { "key": "telemetrySubscriber", @@ -63,4 +64,4 @@ } ] } -} \ No newline at end of file +} diff --git a/platform/telemetry/src/TelemetryCapability.js b/platform/telemetry/src/TelemetryCapability.js index 1fbd12a691..d89b3cd3bf 100644 --- a/platform/telemetry/src/TelemetryCapability.js +++ b/platform/telemetry/src/TelemetryCapability.js @@ -36,6 +36,64 @@ define( getRangeValue: ZERO }; + /** + * Provides metadata about telemetry associated with a + * given domain object. + * + * @typedef TelemetryMetadata + * @property {string} source the machine-readable identifier for + * the source of telemetry data for this object; used by + * {@link TelemetryService} implementations to determine + * whether or not they provide data for this object. + * @property {string} key the machine-readable identifier for + * telemetry data associated with this specific object, + * within that `source`. + * @property {TelemetryDomainMetadata[]} domains supported domain + * options for telemetry data associated with this object, + * to use in interpreting a {@link TelemetrySeries} + * @property {TelemetryRangeMetadata[]} ranges supported range + * options for telemetry data associated with this object, + * to use in interpreting a {@link TelemetrySeries} + */ + + /** + * Provides metadata about range options within a telemetry series. + * Range options describe distinct properties within any given datum + * of a telemetry series; for instance, a telemetry series containing + * both raw and uncalibrated values may provide separate ranges for + * each. + * + * @typedef TelemetryRangeMetadata + * @property {string} key machine-readable identifier for this range + * @property {string} name human-readable name for this range + * @property {string} [units] human-readable units for this range + * @property {string} [format] data format for this range; usually, + * one of `number`, or `string`. If `undefined`, + * should presume to be a `number`. Custom formats + * may be indicated here. + */ + + /** + * Provides metadata about domain options within a telemetry series. + * Domain options describe distinct properties within any given datum + * of a telemtry series; for instance, a telemetry series containing + * both spacecraft event time and earth received times may provide + * separate domains for each. + * + * Domains are typically used to represent timestamps in a telemetry + * series, but more generally may express any property which will + * have unique values for each datum in a series. It is this property + * which makes domains distinct from ranges, as it makes these values + * appropriate and meaningful for use to sort and bound a series. + * + * @typedef TelemetryDomainMetadata + * @property {string} key machine-readable identifier for this range + * @property {string} name human-readable name for this range + * @property {string} [system] machine-readable identifier for the + * time/date system associated with this domain; + * used by {@link DateService} + */ + /** * A telemetry capability provides a means of requesting telemetry * for a specific object, and for unwrapping the response (to get diff --git a/platform/telemetry/src/TelemetryFormatter.js b/platform/telemetry/src/TelemetryFormatter.js index bbd4cf100c..dd434d4ac3 100644 --- a/platform/telemetry/src/TelemetryFormatter.js +++ b/platform/telemetry/src/TelemetryFormatter.js @@ -22,14 +22,13 @@ /*global define,moment*/ define( - ['moment'], - function (moment) { + [], + function () { "use strict"; // Date format to use for domain values; in particular, // use day-of-year instead of month/day - var DATE_FORMAT = "YYYY-DDD HH:mm:ss", - VALUE_FORMAT_DIGITS = 3; + var VALUE_FORMAT_DIGITS = 3; /** * The TelemetryFormatter is responsible for formatting (as text @@ -37,22 +36,31 @@ define( * the range (usually value) of a data series. * @memberof platform/telemetry * @constructor + * @param {FormatService} formatService the service to user to format + * domain values + * @param {string} defaultFormatKey the format to request when no + * format has been otherwise specified */ - function TelemetryFormatter() { + function TelemetryFormatter(formatService, defaultFormatKey) { + this.formatService = formatService; + this.defaultFormat = formatService.getFormat(defaultFormatKey); } /** * Format a domain value. - * @param {number} v the domain value; a timestamp + * @param {number} v the domain value; usually, a timestamp * in milliseconds since start of 1970 - * @param {string} [key] the key which identifies the - * domain; if unspecified or unknown, this will - * be treated as a standard timestamp. + * @param {string} [key] a key which identifies the format + * to use * @returns {string} a textual representation of the * data and time, suitable for display. */ TelemetryFormatter.prototype.formatDomainValue = function (v, key) { - return isNaN(v) ? "" : moment.utc(v).format(DATE_FORMAT); + var formatter = (key === undefined) ? + this.defaultFormat : + this.formatService.getFormat(key); + + return isNaN(v) ? "" : formatter.format(v); }; /** diff --git a/platform/telemetry/test/TelemetryFormatterSpec.js b/platform/telemetry/test/TelemetryFormatterSpec.js index 22f1579059..23c7b95fd4 100644 --- a/platform/telemetry/test/TelemetryFormatterSpec.js +++ b/platform/telemetry/test/TelemetryFormatterSpec.js @@ -27,16 +27,35 @@ define( "use strict"; describe("The telemetry formatter", function () { - var formatter; + var mockFormatService, + mockFormat, + formatter; beforeEach(function () { - formatter = new TelemetryFormatter(); + mockFormatService = + jasmine.createSpyObj("formatService", ["getFormat"]); + mockFormat = jasmine.createSpyObj("format", [ + "validate", + "parse", + "format" + ]); + mockFormatService.getFormat.andReturn(mockFormat); + formatter = new TelemetryFormatter(mockFormatService); }); - it("formats domains using YYYY-DDD style", function () { - expect(formatter.formatDomainValue(402513731000)).toEqual( - "1982-276 17:22:11" - ); + it("formats domains using the formatService", function () { + var testValue = 12321, testResult = "some result"; + mockFormat.format.andReturn(testResult); + + expect(formatter.formatDomainValue(testValue)) + .toEqual(testResult); + expect(mockFormat.format).toHaveBeenCalledWith(testValue); + }); + + it("passes format keys to the formatService", function () { + formatter.formatDomainValue(12321, "someKey"); + expect(mockFormatService.getFormat) + .toHaveBeenCalledWith("someKey"); }); it("formats ranges as values", function () { @@ -44,4 +63,4 @@ define( }); }); } -); \ No newline at end of file +);