UNPKG

@microsoft/sp-dialog

Version:

SharePoint Framework support for displaying dialog boxes

430 lines 19.5 kB
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