mirror of
https://github.com/nasa/openmct.git
synced 2024-12-22 22:42:24 +00:00
[Common UI] Refactor dialogs
Refactor bundle platform/commonUI/dialog such that the concerns of showing dialogs (including showing a single instance thereof) and managing changes to the DOM are more cleanly separated. This simplifies testing and satisfies code style guidelines for this bundle, in preparation for its inclusion among common user interface bundles to be transitioned in WTD-574.
This commit is contained in:
parent
6cce00d601
commit
0faf4c934e
@ -4,7 +4,12 @@
|
|||||||
{
|
{
|
||||||
"key": "dialogService",
|
"key": "dialogService",
|
||||||
"implementation": "DialogService.js",
|
"implementation": "DialogService.js",
|
||||||
"depends": [ "$document", "$compile", "$rootScope", "$timeout", "$q", "$log" ]
|
"depends": [ "overlayService", "$q", "$log" ]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "overlayService",
|
||||||
|
"implementation": "OverlayService.js",
|
||||||
|
"depends": [ "$document", "$compile", "$rootScope" ]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"templates": [
|
"templates": [
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<div class="abs overlay" ng-show="ngModel.visible">
|
<div class="abs overlay">
|
||||||
<div class="abs blocker"></div>
|
<div class="abs blocker"></div>
|
||||||
<div class="abs holder">
|
<div class="abs holder">
|
||||||
<a href=""
|
<a href=""
|
||||||
ng-click="state.cancel()"
|
ng-click="ngModel.cancel()"
|
||||||
class="btn normal outline ui-symbol close">x</a>
|
class="btn normal outline ui-symbol close">x</a>
|
||||||
<div class="abs contents">
|
<div class="abs contents">
|
||||||
<div class="abs top-bar">
|
<div class="abs top-bar">
|
||||||
|
@ -7,33 +7,91 @@ define(
|
|||||||
[],
|
[],
|
||||||
function () {
|
function () {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
// Template to inject into the DOM to show the dialog; really just points to
|
|
||||||
// the overlay-dialog template.
|
|
||||||
var TEMPLATE = "<mct-include ng-model=\"dialog\" key=\"'overlay-dialog'\"></mct-include>";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The dialog service is responsible for handling window-modal
|
* The dialog service is responsible for handling window-modal
|
||||||
* communication with the user, such as displaying forms for user
|
* communication with the user, such as displaying forms for user
|
||||||
* input.
|
* input.
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function DialogService($document, $compile, $rootScope, $timeout, $q, $log) {
|
function DialogService(overlayService, $q, $log) {
|
||||||
var scope;
|
var overlay,
|
||||||
|
dialogVisible = false;
|
||||||
|
|
||||||
// Inject the dialog at the top of the body; this is necessary to
|
// Stop showing whatever overlay is currently active
|
||||||
// ensure that the dialog is positioned appropriately and can fill
|
// (e.g. because the user hit cancel)
|
||||||
// the screen to block other interactions.
|
function dismiss() {
|
||||||
function addContent() {
|
if (overlay) {
|
||||||
scope = $rootScope.$new();
|
overlay.dismiss();
|
||||||
$document.find('body').prepend($compile(TEMPLATE)(scope));
|
}
|
||||||
scope.dialog = { visible: false, value: {} };
|
dialogVisible = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dismiss the dialog; just stop showing it, and release any
|
function getUserInput(formModel, value) {
|
||||||
// form information for garbage collection.
|
// We will return this result as a promise, because user
|
||||||
function dismiss() {
|
// input is asynchronous.
|
||||||
scope.dialog = { visible: false, value: {} };
|
var deferred = $q.defer(),
|
||||||
|
overlayModel;
|
||||||
|
|
||||||
|
// Confirm function; this will be passed in to the
|
||||||
|
// overlay-dialog template and associated with a
|
||||||
|
// OK button click
|
||||||
|
function confirm() {
|
||||||
|
var resultingValue;
|
||||||
|
|
||||||
|
// Temporary workaround, in the absence of a
|
||||||
|
// forms package.
|
||||||
|
try {
|
||||||
|
resultingValue = JSON.parse(overlayModel.value);
|
||||||
|
} catch (e) {
|
||||||
|
resultingValue = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass along the result
|
||||||
|
deferred.resolve(resultingValue);
|
||||||
|
|
||||||
|
// Stop showing the dialog
|
||||||
|
dismiss();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cancel function; this will be passed in to the
|
||||||
|
// overlay-dialog template and associated with a
|
||||||
|
// Cancel or X button click
|
||||||
|
function cancel() {
|
||||||
|
deferred.reject();
|
||||||
|
dismiss();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dialogVisible) {
|
||||||
|
// Only one dialog should be shown at a time.
|
||||||
|
// The application design should be such that
|
||||||
|
// we never even try to do this.
|
||||||
|
$log.warn([
|
||||||
|
"Dialog already showing; ",
|
||||||
|
"unable to show ",
|
||||||
|
formModel.name
|
||||||
|
].join(""));
|
||||||
|
deferred.reject();
|
||||||
|
} else {
|
||||||
|
// To be passed to the overlay-dialog template,
|
||||||
|
// via ng-model
|
||||||
|
overlayModel = {
|
||||||
|
title: formModel.name,
|
||||||
|
message: formModel.message,
|
||||||
|
formModel: formModel,
|
||||||
|
value: JSON.stringify(value),
|
||||||
|
confirm: confirm,
|
||||||
|
cancel: cancel
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add the overlay using the OverlayService, which
|
||||||
|
// will handle actual insertion into the DOM
|
||||||
|
overlay = overlayService.createOverlay(
|
||||||
|
overlayModel,
|
||||||
|
"overlay-dialog"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return deferred.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -48,49 +106,7 @@ define(
|
|||||||
* user input cannot be obtained (for instance,
|
* user input cannot be obtained (for instance,
|
||||||
* because the user cancelled the dialog)
|
* because the user cancelled the dialog)
|
||||||
*/
|
*/
|
||||||
getUserInput: function (formModel, value) {
|
getUserInput: getUserInput
|
||||||
var deferred = $q.defer();
|
|
||||||
|
|
||||||
if (!scope) {
|
|
||||||
addContent();
|
|
||||||
}
|
|
||||||
|
|
||||||
$timeout(function () {
|
|
||||||
if (scope.dialog.visible) {
|
|
||||||
$log.warn([
|
|
||||||
"Dialog already showing; ",
|
|
||||||
"unable to show ",
|
|
||||||
formModel.name
|
|
||||||
].join(""));
|
|
||||||
deferred.reject();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
scope.dialog.visible = true;
|
|
||||||
scope.dialog.title = formModel.name;
|
|
||||||
scope.dialog.message = formModel.message;
|
|
||||||
scope.dialog.formModel = formModel;
|
|
||||||
scope.dialog.value = JSON.stringify(value);
|
|
||||||
|
|
||||||
scope.dialog.confirm = function () {
|
|
||||||
var resultingValue;
|
|
||||||
|
|
||||||
try {
|
|
||||||
resultingValue = JSON.parse(scope.dialog.value);
|
|
||||||
} catch (e) {
|
|
||||||
resultingValue = {};
|
|
||||||
}
|
|
||||||
deferred.resolve(resultingValue);
|
|
||||||
dismiss();
|
|
||||||
};
|
|
||||||
scope.dialog.cancel = function () {
|
|
||||||
deferred.reject();
|
|
||||||
dismiss();
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
return deferred.promise;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
72
platform/commonUI/dialog/src/OverlayService.js
Normal file
72
platform/commonUI/dialog/src/OverlayService.js
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
/*global define*/
|
||||||
|
|
||||||
|
define(
|
||||||
|
[],
|
||||||
|
function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// 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 = '<mct-include ng-model="overlay" key="key"></mct-include>';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The OverlayService is responsible for pre-pending templates to
|
||||||
|
* the body of the document, which is useful for displaying templates
|
||||||
|
* which need to block the full screen.
|
||||||
|
*
|
||||||
|
* This is intended to be used by the DialogService; by design, it
|
||||||
|
* does not have any protections in place to prevent multiple overlays
|
||||||
|
* from being shown at once. (The DialogService does have these
|
||||||
|
* protections, and should be used for most overlay-type interactions,
|
||||||
|
* particularly where a multiple-overlay effect is not specifically
|
||||||
|
* desired).
|
||||||
|
*
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
function OverlayService($document, $compile, $rootScope) {
|
||||||
|
function createOverlay(overlayModel, key) {
|
||||||
|
// Create a new scope for this overlay
|
||||||
|
var scope = $rootScope.$new(),
|
||||||
|
element;
|
||||||
|
|
||||||
|
// Populate the scope; will be passed directly to the template
|
||||||
|
scope.overlay = overlayModel;
|
||||||
|
scope.key = key;
|
||||||
|
|
||||||
|
// Create the overlay element and add it to the document's body
|
||||||
|
element = $compile(TEMPLATE)(scope);
|
||||||
|
$document.find('body').prepend(element);
|
||||||
|
|
||||||
|
// Stop showing the overlay; additionally, release the scope
|
||||||
|
// that it uses.
|
||||||
|
function dismiss() {
|
||||||
|
scope.$destroy();
|
||||||
|
element.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
dismiss: dismiss
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
/**
|
||||||
|
* Add a new overlay to the document. This will be
|
||||||
|
* prepended to the document body; the overlay's
|
||||||
|
* template (as pointed to by the `key` argument) is
|
||||||
|
* responsible for having a useful z-order, and for
|
||||||
|
* blocking user interactions if appropriate.
|
||||||
|
* @param {object} overlayModel the model to pass to the
|
||||||
|
* included overlay template (this will be passed
|
||||||
|
* in via ng-model)
|
||||||
|
* @param {string} key the symbolic key which identifies
|
||||||
|
* the template of the overlay to be shown
|
||||||
|
*/
|
||||||
|
createOverlay: createOverlay
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return OverlayService;
|
||||||
|
}
|
||||||
|
);
|
Loading…
Reference in New Issue
Block a user