@microsoft/sp-dialog
Version:
SharePoint Framework support for displaying dialog boxes
430 lines • 19.5 kB
JavaScript
import { _DialogManagerConfiguration as DialogManagerConfiguration } from '@microsoft/sp-application-base';
import { Guid, Validate } from '@microsoft/sp-core-library';
import { _LogSource, _QosMonitor, _TraceLogger } from '@microsoft/sp-diagnostics';
import * as React from 'react';
import * as ReactDom from 'react-dom';
import AlertDialog from './AlertDialog';
import { _DialogState } from './BaseDialog';
// Alias for easier usage
var DialogState = _DialogState;
import FabricDialogWrapper from './FabricDialogWrapper';
import PromptDialog from './PromptDialog';
import SecondaryDialogProvider from './SecondaryDialogProvider';
export function _getInternalDialogApi(dialog) {
return dialog;
}
/**
* @internal
*/
var DialogManager = /** @class */ (function () {
function DialogManager(config) {
var _this = this;
/**
* Add a request to show a dialog. Requests are handled on FIFO basis.
*
* @returns A void promise that resolves when the dialog is closed, or rejects if the request is rejected or aborted.
*
* @param dialog - The dialog to show
* @param options - A callback that checks if the dialog still wants to be shown. Because there might be a delay
* until the request is approved, the caller might not want to show the dialog anymore by the time it's approved.
* This callback is called before showing the dialog and if returns true, the request is aborted.
*/
this.show = function (dialog, options) {
Validate.isNotNullOrUndefined(dialog, 'dialog');
var _dialog = _getInternalDialogApi(dialog);
var qosMonitor = new _QosMonitor('DialogManager.show');
_TraceLogger.logVerbose(DialogManager._logSource, 'New request to show a dialog has been submitted.');
try {
if (!_dialog._isActive) {
var resolveCallback_1 = function () {
return;
}, rejectCallback_1 = function () {
return;
};
var promise = new Promise(function (resolve, reject) {
resolveCallback_1 = resolve;
rejectCallback_1 = reject;
}).then(function () {
qosMonitor.writeSuccess();
}, function (e) {
qosMonitor.writeExpectedFailure('RequestRejected', e);
});
var id = Guid.newGuid();
_this._requests.push({ id: id, dialog: dialog, resolve: resolveCallback_1, reject: rejectCallback_1, options: options });
_dialog._requestAck(id);
_dialog._setState(DialogState.Pending);
// Process next request asynchronously
void Promise.resolve(undefined).then(function () {
_this._processNextRequest();
});
return promise;
}
else {
qosMonitor.writeExpectedFailure('DuplicateRequest');
// VSO #385434 Localize sp-dialog errors
var error = new Error('Cannot accept new requests for active dialogs.');
_TraceLogger.logError(DialogManager._logSource, error);
return Promise.reject(error);
}
}
catch (e) {
qosMonitor.writeUnexpectedFailure('UnhandledError', e);
return Promise.reject(e);
}
};
/**
* Abort the request for the given dialog.
* Note: Active dialogs cannot be aborted because they are alreay approved.
*/
this._abort = function (dialog) {
var _dialog = _getInternalDialogApi(dialog);
if (dialog && !_dialog._isActive) {
_dialog._setState(DialogState.New);
_this._removeRequest(_dialog._requestId, false);
}
};
/**
* This is called when the dialog rejects is onOpen promise, which is a special case that we treat like abort but
* we also have to close the modal and NOT call onAfterClose().
*/
this._rejectOnOpen = function (dialog) {
var _dialog = _getInternalDialogApi(dialog);
if (dialog) {
void _this._getUiComponent(dialog)
.close()
.then(function () {
// Setting state to 'New' because we're aborting
_dialog._setState(DialogState.New);
_this._removeRequest(_dialog._requestId, false);
});
}
};
/**
* Close the given dialog
*/
this._close = function (dialog) {
Validate.isNotNullOrUndefined(dialog, 'dialog');
var _dialog = _getInternalDialogApi(dialog);
var qosMonitor = new _QosMonitor('DialogManager.close');
if (_dialog._isActive) {
// Visually hide the dialog
var closePromise = _this._getUiComponent(dialog)
.close()
.then(function () {
_dialog.onAfterClose();
_dialog._setState(DialogState.Closed);
_this._removeRequest(_dialog._requestId, true);
})
.then(function () {
qosMonitor.writeSuccess();
}, function (e) {
qosMonitor.writeExpectedFailure('RequestRejected', e);
});
void closePromise.then(function () {
if (_this._pendingRequests.length > 0 || _this._hasHiddenDialog) {
_this._processNextRequest();
}
});
return closePromise;
}
else {
// Already closed, so resolve the promise
qosMonitor.writeSuccess();
return Promise.resolve(undefined);
}
};
/**
* Process the request on top of the queue.
*/
this._processNextRequest = function () {
if (_this.isShowingDialog) {
return;
}
var qosMonitor = new _QosMonitor('DialogManager.processRequest');
try {
for (var _i = 0, _a = _this._requests; _i < _a.length; _i++) {
var request = _a[_i];
var _dialog = _getInternalDialogApi(request.dialog);
var abort = false;
if (!request.isSecondary && request.options) {
var confirmOpen = request.options.confirmOpen ? request.options.confirmOpen() : true;
abort = !confirmOpen;
}
if (abort) {
// VSO #385434 Localize sp-dialog errors
var message = 'The request to show the dialog was aborted.';
_TraceLogger.logVerbose(DialogManager._logSource, message);
request.reject(new Error(message));
_this._abort(request.dialog);
}
else if (_dialog._state === DialogState.Approved || _dialog._state === DialogState.Hidden) {
_this._open(request.dialog);
}
else if (_dialog._state === DialogState.Pending) {
if (_this._config.allowDialogs) {
_dialog._setState(DialogState.Approved);
_this._open(request.dialog);
}
else {
// VSO #385434 Localize sp-dialog errors
var error = new Error('The application cannot accept dialogs at the moment.');
_TraceLogger.logError(DialogManager._logSource, error);
request.reject(error);
_dialog._setState(DialogState.Rejected);
_this._removeRequest(request.id, false);
}
break;
}
}
qosMonitor.writeSuccess();
}
catch (e) {
_TraceLogger.logErrorData({
source: DialogManager._logSource,
error: e
});
qosMonitor.writeUnexpectedFailure('UnhandledError', e);
}
};
this._removeRequest = function (reqId, shouldResolve) {
if (reqId) {
for (var i = 0; i < _this._requests.length; i++) {
var req = _this._requests[i];
if (req.id === reqId) {
if (shouldResolve) {
req.resolve();
}
_this._requests.splice(i, 1);
}
}
}
};
this._hide = function (dialog) {
var _dialog = _getInternalDialogApi(dialog);
if (dialog && _dialog._state === DialogState.Open) {
// Visually hide the dialog
void _this._getUiComponent(dialog)
.close()
.then(function () {
_dialog._setState(DialogState.Hidden);
});
}
};
this._unhide = function (dialog) {
var _dialog = _getInternalDialogApi(dialog);
if (dialog && _dialog._state === DialogState.Hidden) {
// Visually hide the dialog
void _this._getUiComponent(dialog).open(dialog);
_dialog._setState(DialogState.Open);
}
};
this._open = function (dialog) {
var _dialog = _getInternalDialogApi(dialog);
if (_dialog._state === DialogState.Hidden) {
_this._unhide(dialog);
return true;
}
if (_dialog._state === DialogState.Approved) {
if (_this.isShowingDialog && dialog.isSecondary) {
// A child dialog is overlaying the current dialog. So, hide the main dialog and show the child dialog
_this._hide(_this._currentlyOpenDialogRequest.dialog);
// For the first render, create the react component that owns the modal
if (!_this._childUiComponent && _this._childDialogDiv) {
// eslint-disable-next-line @rushstack/pair-react-dom-render-unmount
ReactDom.render(React.createElement(FabricDialogWrapper, {
closeCallback: _this._close,
ref: function (c) {
_this._childUiComponent = c;
}
}), _this._childDialogDiv);
}
void _this._childUiComponent.open(dialog).then(function () {
_dialog._setState(DialogState.Open);
});
return true;
}
else {
// For the first render, create the react component that owns the modal
if (!_this._mainUiComponent && _this._mainDialogDiv) {
// eslint-disable-next-line @rushstack/pair-react-dom-render-unmount
ReactDom.render(React.createElement(FabricDialogWrapper, {
closeCallback: _this._close,
ref: function (c) {
_this._mainUiComponent = c;
}
}), _this._mainDialogDiv);
}
void _this._mainUiComponent.open(dialog).then(function () {
_dialog._setState(DialogState.Open);
});
return true;
}
}
return false;
};
Validate.isNotNullOrUndefined(config, 'DialogManager Configuration');
Validate.isNotNullOrUndefined(config.domElement, 'Dialog Container');
this._container = config.domElement;
this._mainDialogDiv = document.createElement('DIV');
this._childDialogDiv = document.createElement('DIV');
this._container.appendChild(this._mainDialogDiv);
this._container.appendChild(this._childDialogDiv);
this._requests = [];
}
Object.defineProperty(DialogManager, "instance", {
/**
* Get the singleton instance of DialogManager. It looks for a singleton instance of DialogManagerConfiguration
* to initialize and throws if it is not found. The configuration instance is set on the page by the application.
*/
get: function () {
if (!this._instance) {
this._instance = new DialogManager(DialogManagerConfiguration.instance);
}
return this._instance;
},
enumerable: false,
configurable: true
});
/**
* {@inheritDoc Dialog.alert}
*/
DialogManager.prototype.alert = function (message, options) {
return this.show(new AlertDialog(message), options);
};
/**
* {@inheritDoc Dialog.prompt}
*/
DialogManager.prototype.prompt = function (message, options) {
var _this = this;
var promise = new Promise(function (resolve) {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
_this.show(new PromptDialog(message, resolve), options);
});
return promise;
};
Object.defineProperty(DialogManager.prototype, "isShowingDialog", {
/**
* If the manager has an open dialog
*/
get: function () {
return this._activeDialogs.filter(function (d) { return _getInternalDialogApi(d)._state === DialogState.Open; }).length > 0;
},
enumerable: false,
configurable: true
});
DialogManager.prototype.createSecondaryDialogProvider = function (ownerRequestId) {
return new SecondaryDialogProvider(this, ownerRequestId);
};
/**
* This method is used by secondary dialog manager to show a secondary dialog.
*/
DialogManager.prototype._showSecondary = function (dialog, ownerRequestId, options) {
Validate.isNotNullOrUndefined(dialog, 'dialog');
Validate.isNotNullOrUndefined(ownerRequestId, 'ownerRequestId');
var _dialog = _getInternalDialogApi(dialog);
var qosMonitor = new _QosMonitor('DialogManager.showSecondary');
_TraceLogger.logVerbose(DialogManager._logSource, 'A request to show a secondary dialog has been submitted.');
try {
// Verify the permission
if (this._hasPermissionToShow(ownerRequestId)) {
// create a pre-approved request for this dialog and put in top of the request queue
var resolveCallback_2 = function () {
return;
}, rejectCallback_2 = function () {
return;
};
var promise = new Promise(function (resolve, reject) {
resolveCallback_2 = resolve;
rejectCallback_2 = reject;
}).then(function () {
qosMonitor.writeSuccess();
}, function (e) {
qosMonitor.writeExpectedFailure('RequestRejected', e);
});
var id = Guid.newGuid();
var isSecondary = true;
this._requests.unshift({
id: id,
dialog: dialog,
resolve: resolveCallback_2,
reject: rejectCallback_2,
isSecondary: isSecondary,
options: options
});
_dialog._requestAck(id, true);
_dialog._setState(DialogState.Approved);
this._open(dialog);
return promise;
}
else {
var error = new Error('Only an active dialog can show a secondary dialog.');
_TraceLogger.logError(DialogManager._logSource, error);
qosMonitor.writeExpectedFailure('PermissionDenied');
return Promise.reject(error);
}
}
catch (e) {
_TraceLogger.logErrorData({
source: DialogManager._logSource,
error: e
});
qosMonitor.writeUnexpectedFailure('UnhandledError', e);
return Promise.reject(e);
}
};
Object.defineProperty(DialogManager.prototype, "_activeDialogs", {
get: function () {
return this._requests.filter(function (r) { return _getInternalDialogApi(r.dialog)._isActive; }).map(function (r) { return r.dialog; });
},
enumerable: false,
configurable: true
});
Object.defineProperty(DialogManager.prototype, "_pendingRequests", {
get: function () {
return this._requests.filter(function (r) { return _getInternalDialogApi(r.dialog)._state === DialogState.Pending; });
},
enumerable: false,
configurable: true
});
Object.defineProperty(DialogManager.prototype, "_hasHiddenDialog", {
get: function () {
return (this._requests.filter(function (r) { return _getInternalDialogApi(r.dialog)._state === DialogState.Hidden; }).length > 0);
},
enumerable: false,
configurable: true
});
Object.defineProperty(DialogManager.prototype, "_currentlyOpenDialogRequest", {
get: function () {
return this._requests.filter(function (r) { return _getInternalDialogApi(r.dialog)._state === DialogState.Open; })[0];
},
enumerable: false,
configurable: true
});
Object.defineProperty(DialogManager.prototype, "_config", {
get: function () {
return DialogManagerConfiguration.instance;
},
enumerable: false,
configurable: true
});
DialogManager.prototype._getUiComponent = function (dialog) {
return dialog.isSecondary ? this._childUiComponent : this._mainUiComponent;
};
/**
* SecondaryDialogManager passes the request id for the open (primary) dialog. This method checks if that request id
* is indeed for the currently open dialog to make sure the secondary dialog has permission to show.
*/
DialogManager.prototype._hasPermissionToShow = function (requestId) {
var request = this._getRequestById(requestId);
return (request &&
_getInternalDialogApi(request.dialog)._isActive &&
!this._currentlyOpenDialogRequest.isSecondary);
};
DialogManager.prototype._getRequestById = function (id) {
return this._requests.filter(function (r) { return r.id.equals(id); })[0];
};
DialogManager._logSource = _LogSource.create('DialogManager');
return DialogManager;
}());
export default DialogManager;
//# sourceMappingURL=DialogManager.js.map