UNPKG

@microsoft/sp-dialog

Version:

SharePoint Framework support for displaying dialog boxes

344 lines 14.4 kB
import { _LogSource, _TraceLogger } from '@microsoft/sp-diagnostics'; import DialogManager from './DialogManager'; /** * The states for our internal state machine that manages dialogs * * @internal */ export var _DialogState; (function (_DialogState) { /** * State: New * Description: The dialog state after construction * Next Possible States: Pending */ _DialogState[_DialogState["New"] = 0] = "New"; /** * State: Pending * Description: The dialog manager has acknowledged the request to show * Next Possible States: Rejected, Aborted, Approved */ _DialogState[_DialogState["Pending"] = 1] = "Pending"; /** * State: Rejected * Description: The dialog manager has rejected the request to show * Next Possible States: Pending (by sending a new request) */ _DialogState[_DialogState["Rejected"] = 2] = "Rejected"; /** * State: Aborted * Description: The dialog has aborted its request to show * Next Possible States: Pending (by sending a new request) */ _DialogState[_DialogState["Aborted"] = 3] = "Aborted"; /** * State: Approved * Description: The dialog manager has approved the request to show * Next Possible States: Open */ _DialogState[_DialogState["Approved"] = 4] = "Approved"; /** * State: Open * Description: The dialog is visually open * Next Possible States: Closed, Hidden */ _DialogState[_DialogState["Open"] = 5] = "Open"; /** * State: Closed * Description: The dialog is closed (can only happen after Open) * Next Possible States: Pending (by sending a new request) */ _DialogState[_DialogState["Closed"] = 6] = "Closed"; /** * State: Hidden * Description: The dialog is hidden behind a secondary dialog * Next Possible States: Open */ _DialogState[_DialogState["Hidden"] = 7] = "Hidden"; })(_DialogState || (_DialogState = {})); /** * The base class for implementing dialogs in SharePoint Framework. This provides a supported way for showing * dialogs to the user inside SharePoint Framework components. * * @remarks * Extend this class to create dialogs for SharePoint Framework. By following the guidelines in implementation, * the framework can ensure that the dialogs are shown in a user-friendly manner. While the content of the dialog is * controlled by this class by implementing the render method, the framework can decide when to show the dialog and * how to render the overlay and modal. The application on the page can also have control on allowing dialogs to show. * Refer to the documentation of the individual methods and properties to learn more about how to extend and use this * class. * * @public */ var BaseDialog = /** @class */ (function () { /** * Constructor for the `BaseDialog` class. * @remarks * * It is important to keep the constructor lightweight. Use `onBeforeOpen()` for doing heavy-weight initialization * that is needed for rendering the dialog such as resource allocations and asynchronous calls to fetch data. You * can use the constructor to set initial parameters of your dialog such as references to resources in your * application. The reason for this is that dialogs are usually constructed upon a UI event e.g. a button click, * but the dialog may not always be shown right after construction. Keeping the constructor lightweight ensures * smooth user experience and avoids doing throw-away work in case the dialog is not shown later e.g. if * the framework rejects it. Another benefit of doing this is avoiding memory leaks by doing all the allocations and * disposals in symmetric `onBeforeOpen()` and `onAfterClose()` events. If you allocate resources in the * constructor, you have a memory leak because there is no guarantee onAfterClose() will get called, * and onAfterClose() is your only opportunity to dispose. * * Example: * ``` * constructor(cacheReference: any) { * super(); * * this._cache = cacheReference; // This is okay. Keeping a reference to my internal cache. * this._cache.refresh(); // This is bad practice. * // If you need to refresh the cache (or fetch data) for rendering, do it in onBeforeOpen() * } * ``` * * @param config - the dialog configuration that affects how the dialog is displayed * */ function BaseDialog(config) { this._config = config || {}; this._state = _DialogState.New; this._isSecondary = false; this.close = this.close.bind(this); this.render = this.render.bind(this); } Object.defineProperty(BaseDialog.prototype, "_configuration", { /** * The dialog configuration * * @internal */ get: function () { return this._config; }, enumerable: false, configurable: true }); Object.defineProperty(BaseDialog.prototype, "isSecondary", { /** * If the dialog is a secondary dialog. This means that there is another dialog hidden behind this dialog. */ get: function () { return this._isSecondary; }, enumerable: false, configurable: true }); Object.defineProperty(BaseDialog.prototype, "isOpen", { /** * If the dialog is visually open. This returns true during onBeforeOpen() because there is a visual component. * It returns false when the dialog is hidden. */ get: function () { return this._state === _DialogState.Open; }, enumerable: false, configurable: true }); Object.defineProperty(BaseDialog.prototype, "isHidden", { /** * If the dialog is visually hidden. * * @remarks * This happens when the dialog goes behind a secondary dialog. * Note that this is different from closed, because the dialog still has the permission to show and will * eventually unhide. This returns false if the dialog is closed. */ get: function () { return this._state === _DialogState.Hidden; }, enumerable: false, configurable: true }); Object.defineProperty(BaseDialog.prototype, "secondaryDialogProvider", { /** * An active dialog is permitted to show a secondary dialog on top of itself. By design, only two layers of * dialogs are permitted. * * @remarks * Secondary dialogs do not have to wait for permission and will immediately be shown or rejected. All calls to * show a secondary dialog reject while there is already a secondary dialog showing. * This property may be undefined if a secondary dialog is not available i.e. the current dialog is secondary itself * or the dialog is not active. */ get: function () { if (this.isSecondary) { return undefined; } if (!this._secondaryDialogProvider && this._requestId) { this._secondaryDialogProvider = DialogManager.instance.createSecondaryDialogProvider(this._requestId); } return this._secondaryDialogProvider; }, enumerable: false, configurable: true }); /** * Request the framework to show this dialog. * * @returns A promise that resolves once the dialog is successfully closed (after being shown). * The promise rejects if the request is rejected or aborted. * * @param options - Dialog showing options. See {@link IDialogShowOptions} for more information. */ BaseDialog.prototype.show = function (options) { if (DialogManager.instance) { return DialogManager.instance.show(this, options); } else { return Promise.reject(new Error('This application is not configured to accept dialogs.')); } }; /** * Close the dialog. * * @remarks * This will void the permission to show for this dialog. Every dialog should have a mechanism to * eventually close to avoid blocking the user interface. If called on an inactive dialog it will abort the request * to show. * * @returns A promise that resolves when the dialog is visually closed, or if it was already closed */ BaseDialog.prototype.close = function () { if (DialogManager.instance) { if (this._isActive) { // Note: onAfterClose() is called by the manager return DialogManager.instance._close(this); } else { DialogManager.instance._abort(this); } } return Promise.resolve(undefined); }; /** * Called by the manager after the modal is open to render the dialog content. * This internally calls onBeforeOpen() and returns a promise that resolves only if the render was successful. * If onBeforeOpen() is rejected or dialog is closed while rendering, the returned promise will reject. * * @internal */ BaseDialog.prototype._render = function (container) { var _this = this; this._domElement = container; if (!this._renderPromise) { var rejectRender_1, resolveRender_1; this._renderPromise = new Promise(function (resolve, reject) { resolveRender_1 = resolve; rejectRender_1 = reject; }); this._closedWhileRendering = false; this.onBeforeOpen() .then(function () { if (!_this._closedWhileRendering) { _this.render(); resolveRender_1(); } else { rejectRender_1(); } }) .catch(function () { // At this point the modal is opened by framework but the dialog itself rejected opening DialogManager.instance._rejectOnOpen(_this); rejectRender_1(); }); } return this._renderPromise; }; /** * Called by the manager to change the state of the dialog. * Note: State management is all done by the manager using this method. Do not call this method from this class. * If you want to do something upon state change, do it in this method by checking the newState. * * @internal */ BaseDialog.prototype._setState = function (newState) { if (newState === _DialogState.Closed) { this._secondaryDialogProvider = undefined; if (this._renderPromise) { this._closedWhileRendering = true; this._renderPromise = undefined; } } _TraceLogger.logVerboseData({ source: BaseDialog._logSource, message: "Dialog state changed from '".concat(_DialogState[this._state], "' to '").concat(_DialogState[newState], "'.") }); this._state = newState; }; /** * Called by the manager to acknowledging a show request and sets some parameters that active dialogs need * * @param requestId - A Guid which is unique string for the manager to identify the request for this dialog * @param isSecondary - If the dialog is secondary (called via secondary dialog API) * * @internal */ BaseDialog.prototype._requestAck = function (requestId, isSecondary) { this._requestId = requestId; this._isSecondary = !!isSecondary; }; Object.defineProperty(BaseDialog.prototype, "domElement", { /** * Use this property to access the container element provided by the framework for rendering. * * @remarks * See {@link BaseDialog.render} for more information on rendering. */ get: function () { return this._domElement; }, enumerable: false, configurable: true }); /** * This method is called before the render method and can be overridden to make preparations for rendering. * The loading indicator is displayed during the lifetime of this method. * virtual * @remarks * All resource allocations in onBeforeOpen() should be properly disposed in `onAfterClose()` to a avoid memory leak. * * @returns A promise that resolves when the operations are done and the dialog is ready to render. If the * promise is rejected, the dialog will not be rendered and `onAfterClose()` will not be called. */ BaseDialog.prototype.onBeforeOpen = function () { return Promise.resolve(undefined); }; /** * This method is called after the dialog is visually closed and gives an opportunity for doing clean up. * @remarks * The dialog lifecycle completes after closing and there should be no resources left inside the object. Even though * the dialog may be revived again for a new lifecycle using show() method, this is considered a whole new lifecycle * that should reallocate its own resources. If there are any resources that you would like to keep for multiple * lifecycles, consider allocating it outside the dialog object and passing its reference to the dialog constructor. */ BaseDialog.prototype.onAfterClose = function () { // nothing to do. }; Object.defineProperty(BaseDialog.prototype, "_isActive", { /** * A dialog is active if it is in one of these states: * - The framework has approved to show it and has started the opening process * - It is open and showing * - It is visually hidden behind a secondary dialog * * When a dialog is active, the framework considers that dialog to have permission to show until it is closed. */ get: function () { return (this._state === _DialogState.Approved || this._state === _DialogState.Open || this._state === _DialogState.Hidden); }, enumerable: false, configurable: true }); BaseDialog._logSource = _LogSource.create('BaseDialog'); return BaseDialog; }()); export default BaseDialog; //# sourceMappingURL=BaseDialog.js.map