UNPKG

@theia/core

Version:

Theia is a cloud & desktop IDE framework implemented in TypeScript.

491 lines • 19.4 kB
"use strict"; // ***************************************************************************** // Copyright (C) 2017 TypeFox and others. // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License v. 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0. // // This Source Code may also be made available under the following Secondary // Licenses when the conditions for such availability set forth in the Eclipse // Public License v. 2.0 are satisfied: GNU General Public License, version 2 // with the GNU Classpath Exception which is available at // https://www.gnu.org/software/classpath/license.html. // // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var __param = (this && this.__param) || function (paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); } }; var DialogOverlayService_1; Object.defineProperty(exports, "__esModule", { value: true }); exports.SingleTextInputDialog = exports.SingleTextInputDialogProps = exports.confirmExitWithOrWithoutSaving = exports.ConfirmSaveDialog = exports.ConfirmSaveDialogProps = exports.confirmExit = exports.ConfirmDialog = exports.ConfirmDialogProps = exports.MessageDialogProps = exports.AbstractDialog = exports.DialogOverlayService = exports.Dialog = exports.DialogError = exports.DialogProps = void 0; const inversify_1 = require("inversify"); const common_1 = require("../common"); const keys_1 = require("./keyboard/keys"); const widgets_1 = require("./widgets"); let DialogProps = class DialogProps { }; DialogProps = __decorate([ (0, inversify_1.injectable)() ], DialogProps); exports.DialogProps = DialogProps; var DialogError; (function (DialogError) { function getResult(error) { if (typeof error === 'string') { return !error.length; } if (typeof error === 'boolean') { return error; } return error.result; } DialogError.getResult = getResult; function getMessage(error) { if (typeof error === 'string') { return error; } if (typeof error === 'boolean') { return ''; } return error.message; } DialogError.getMessage = getMessage; })(DialogError = exports.DialogError || (exports.DialogError = {})); var Dialog; (function (Dialog) { Dialog.YES = common_1.nls.localizeByDefault('Yes'); Dialog.NO = common_1.nls.localizeByDefault('No'); Dialog.OK = common_1.nls.localizeByDefault('OK'); Dialog.CANCEL = common_1.nls.localizeByDefault('Cancel'); })(Dialog = exports.Dialog || (exports.Dialog = {})); let DialogOverlayService = DialogOverlayService_1 = class DialogOverlayService { constructor() { // eslint-disable-next-line @typescript-eslint/no-explicit-any this.dialogs = []; this.documents = []; } static get() { return DialogOverlayService_1.INSTANCE; } initialize() { DialogOverlayService_1.INSTANCE = this; } // eslint-disable-next-line @typescript-eslint/no-explicit-any get currentDialog() { return this.dialogs[0]; } // eslint-disable-next-line @typescript-eslint/no-explicit-any push(dialog) { if (this.documents.findIndex(document => document === dialog.node.ownerDocument) < 0) { (0, widgets_1.addKeyListener)(dialog.node.ownerDocument.body, keys_1.Key.ENTER, e => this.handleEnter(e)); (0, widgets_1.addKeyListener)(dialog.node.ownerDocument.body, keys_1.Key.ESCAPE, e => this.handleEscape(e)); this.documents.push(dialog.node.ownerDocument); } this.dialogs.unshift(dialog); return common_1.Disposable.create(() => { const index = this.dialogs.indexOf(dialog); if (index > -1) { this.dialogs.splice(index, 1); } }); } handleEscape(event) { const dialog = this.currentDialog; if (dialog) { return dialog['handleEscape'](event); } return false; } handleEnter(event) { const dialog = this.currentDialog; if (dialog) { return dialog['handleEnter'](event); } return false; } }; DialogOverlayService = DialogOverlayService_1 = __decorate([ (0, inversify_1.injectable)(), __metadata("design:paramtypes", []) ], DialogOverlayService); exports.DialogOverlayService = DialogOverlayService; let AbstractDialog = class AbstractDialog extends widgets_1.BaseWidget { constructor(props, options) { super(options); this.props = props; this.validateCancellationSource = new common_1.CancellationTokenSource(); this.acceptCancellationSource = new common_1.CancellationTokenSource(); this.id = 'theia-dialog-shell'; this.addClass('dialogOverlay'); this.toDispose.push(common_1.Disposable.create(() => { if (this.reject) { widgets_1.Widget.detach(this); } })); const container = this.node.ownerDocument.createElement('div'); container.classList.add('dialogBlock'); if (props.maxWidth === undefined) { container.setAttribute('style', 'max-width: none'); } else if (props.maxWidth < 400) { container.setAttribute('style', `max-width: ${props.maxWidth}px; min-width: 0px`); } else { container.setAttribute('style', `max-width: ${props.maxWidth}px`); } this.node.appendChild(container); const titleContentNode = this.node.ownerDocument.createElement('div'); titleContentNode.classList.add('dialogTitle'); container.appendChild(titleContentNode); this.titleNode = this.node.ownerDocument.createElement('div'); this.titleNode.textContent = props.title; titleContentNode.appendChild(this.titleNode); this.closeCrossNode = this.node.ownerDocument.createElement('i'); this.closeCrossNode.classList.add(...(0, widgets_1.codiconArray)('close', true)); this.closeCrossNode.classList.add('closeButton'); titleContentNode.appendChild(this.closeCrossNode); this.contentNode = this.node.ownerDocument.createElement('div'); this.contentNode.classList.add('dialogContent'); if (props.wordWrap !== undefined) { this.contentNode.setAttribute('style', `word-wrap: ${props.wordWrap}`); } container.appendChild(this.contentNode); this.controlPanel = this.node.ownerDocument.createElement('div'); this.controlPanel.classList.add('dialogControl'); container.appendChild(this.controlPanel); this.errorMessageNode = this.node.ownerDocument.createElement('div'); this.errorMessageNode.classList.add('error'); this.errorMessageNode.setAttribute('style', 'flex: 2'); this.controlPanel.appendChild(this.errorMessageNode); this.update(); } appendCloseButton(text = Dialog.CANCEL) { return this.closeButton = this.appendButton(text, false); } appendAcceptButton(text = Dialog.OK) { return this.acceptButton = this.appendButton(text, true); } appendButton(text, primary) { const button = this.createButton(text); this.controlPanel.appendChild(button); button.classList.add(primary ? 'main' : 'secondary'); return button; } createButton(text) { const button = document.createElement('button'); button.classList.add('theia-button'); button.textContent = text; return button; } onAfterAttach(msg) { super.onAfterAttach(msg); if (this.closeButton) { this.addCloseAction(this.closeButton, 'click'); } if (this.acceptButton) { this.addAcceptAction(this.acceptButton, 'click'); } this.addCloseAction(this.closeCrossNode, 'click'); // TODO: use DI always to create dialog instances this.toDisposeOnDetach.push(DialogOverlayService.get().push(this)); } handleEscape(event) { this.close(); } handleEnter(event) { if (event.target instanceof HTMLTextAreaElement) { return false; } this.accept(); } onActivateRequest(msg) { super.onActivateRequest(msg); if (this.acceptButton) { this.acceptButton.focus(); } } open() { if (this.resolve) { return Promise.reject(new Error('The dialog is already opened.')); } this.activeElement = this.node.ownerDocument.activeElement; return new Promise((resolve, reject) => { this.resolve = resolve; this.reject = reject; this.toDisposeOnDetach.push(common_1.Disposable.create(() => { this.resolve = undefined; this.reject = undefined; })); widgets_1.Widget.attach(this, this.node.ownerDocument.body); this.activate(); }); } close() { if (this.resolve) { if (this.activeElement) { this.activeElement.focus({ preventScroll: true }); } this.resolve(undefined); } this.activeElement = undefined; super.close(); } onUpdateRequest(msg) { super.onUpdateRequest(msg); this.validate(); } async validate() { if (!this.resolve) { return; } this.validateCancellationSource.cancel(); this.validateCancellationSource = new common_1.CancellationTokenSource(); const token = this.validateCancellationSource.token; const value = this.value; const error = await this.isValid(value, 'preview'); if (token.isCancellationRequested) { return; } this.setErrorMessage(error); } async accept() { if (!this.resolve) { return; } this.acceptCancellationSource.cancel(); this.acceptCancellationSource = new common_1.CancellationTokenSource(); const token = this.acceptCancellationSource.token; const value = this.value; const error = await this.isValid(value, 'open'); if (token.isCancellationRequested) { return; } if (!DialogError.getResult(error)) { this.setErrorMessage(error); } else { this.resolve(value); widgets_1.Widget.detach(this); } } /** * Return a string of zero-length or true if valid. */ isValid(value, mode) { return ''; } setErrorMessage(error) { if (this.acceptButton) { this.acceptButton.disabled = !DialogError.getResult(error); } this.errorMessageNode.innerText = DialogError.getMessage(error); } addAction(element, callback, ...additionalEventTypes) { this.addKeyListener(element, keys_1.Key.ENTER, callback, ...additionalEventTypes); } addCloseAction(element, ...additionalEventTypes) { this.addAction(element, () => this.close(), ...additionalEventTypes); } addAcceptAction(element, ...additionalEventTypes) { this.addAction(element, () => this.accept(), ...additionalEventTypes); } }; AbstractDialog = __decorate([ (0, inversify_1.injectable)(), __metadata("design:paramtypes", [DialogProps, Object]) ], AbstractDialog); exports.AbstractDialog = AbstractDialog; let MessageDialogProps = class MessageDialogProps extends DialogProps { }; MessageDialogProps = __decorate([ (0, inversify_1.injectable)() ], MessageDialogProps); exports.MessageDialogProps = MessageDialogProps; let ConfirmDialogProps = class ConfirmDialogProps extends MessageDialogProps { }; ConfirmDialogProps = __decorate([ (0, inversify_1.injectable)() ], ConfirmDialogProps); exports.ConfirmDialogProps = ConfirmDialogProps; let ConfirmDialog = class ConfirmDialog extends AbstractDialog { constructor(props) { super(props); this.props = props; this.confirmed = true; this.contentNode.appendChild(this.createMessageNode(this.props.msg)); this.appendCloseButton(props.cancel); this.appendAcceptButton(props.ok); } onCloseRequest(msg) { super.onCloseRequest(msg); this.confirmed = false; this.accept(); } get value() { return this.confirmed; } createMessageNode(msg) { if (typeof msg === 'string') { const messageNode = this.node.ownerDocument.createElement('div'); messageNode.textContent = msg; return messageNode; } return msg; } }; ConfirmDialog = __decorate([ __param(0, (0, inversify_1.inject)(ConfirmDialogProps)), __metadata("design:paramtypes", [ConfirmDialogProps]) ], ConfirmDialog); exports.ConfirmDialog = ConfirmDialog; async function confirmExit() { const safeToExit = await new ConfirmDialog({ title: common_1.nls.localizeByDefault('Are you sure you want to quit?'), msg: common_1.nls.localize('theia/core/quitMessage', 'Any unsaved changes will not be saved.'), ok: Dialog.YES, cancel: Dialog.NO, }).open(); return safeToExit === true; } exports.confirmExit = confirmExit; class ConfirmSaveDialogProps extends MessageDialogProps { } exports.ConfirmSaveDialogProps = ConfirmSaveDialogProps; // Dialog prompting the user to confirm whether they wish to save changes or not let ConfirmSaveDialog = class ConfirmSaveDialog extends AbstractDialog { constructor(props) { super(props); this.props = props; this.result = false; // Append message and buttons to the dialog this.contentNode.appendChild(this.createMessageNode(this.props.msg)); this.closeButton = this.appendButtonAndSetResult(props.cancel, false); this.appendButtonAndSetResult(props.dontSave, false, false); this.acceptButton = this.appendButtonAndSetResult(props.save, true, true); } get value() { return this.result; } createMessageNode(msg) { if (typeof msg === 'string') { const messageNode = document.createElement('div'); messageNode.textContent = msg; return messageNode; } return msg; } appendButtonAndSetResult(text, primary, result) { const button = this.appendButton(text, primary); button.addEventListener('click', () => { this.result = result; this.accept(); }); return button; } }; ConfirmSaveDialog = __decorate([ __param(0, (0, inversify_1.inject)(ConfirmSaveDialogProps)), __metadata("design:paramtypes", [ConfirmSaveDialogProps]) ], ConfirmSaveDialog); exports.ConfirmSaveDialog = ConfirmSaveDialog; // Asks the user to confirm whether they want to exit with or without saving the changes async function confirmExitWithOrWithoutSaving(captionsToSave, performSave) { const div = document.createElement('div'); div.innerText = common_1.nls.localizeByDefault("Your changes will be lost if you don't save them."); if (captionsToSave.length > 0) { const span = document.createElement('span'); span.appendChild(document.createElement('br')); captionsToSave.forEach(cap => { const b = document.createElement('b'); b.innerText = cap; span.appendChild(b); span.appendChild(document.createElement('br')); }); span.appendChild(document.createElement('br')); div.appendChild(span); const result = await new ConfirmSaveDialog({ title: common_1.nls.localizeByDefault('Do you want to save the changes to the following {0} files?', captionsToSave.length), msg: div, dontSave: common_1.nls.localizeByDefault("Don't Save"), save: common_1.nls.localizeByDefault('Save All'), cancel: Dialog.CANCEL }).open(); if (result) { await performSave(); } return result !== undefined; } else { // fallback if not passed with an empty caption-list. return confirmExit(); } } exports.confirmExitWithOrWithoutSaving = confirmExitWithOrWithoutSaving; let SingleTextInputDialogProps = class SingleTextInputDialogProps extends DialogProps { }; SingleTextInputDialogProps = __decorate([ (0, inversify_1.injectable)() ], SingleTextInputDialogProps); exports.SingleTextInputDialogProps = SingleTextInputDialogProps; let SingleTextInputDialog = class SingleTextInputDialog extends AbstractDialog { constructor(props) { super(props); this.props = props; this.inputField = document.createElement('input'); this.inputField.type = 'text'; this.inputField.className = 'theia-input'; this.inputField.spellcheck = false; this.inputField.setAttribute('style', 'flex: 0;'); this.inputField.placeholder = props.placeholder || ''; this.inputField.value = props.initialValue || ''; if (props.initialSelectionRange) { this.inputField.setSelectionRange(props.initialSelectionRange.start, props.initialSelectionRange.end, props.initialSelectionRange.direction); } else { this.inputField.select(); } this.contentNode.appendChild(this.inputField); this.controlPanel.removeChild(this.errorMessageNode); this.contentNode.appendChild(this.errorMessageNode); this.appendAcceptButton(props.confirmButtonLabel); } get value() { return this.inputField.value; } isValid(value, mode) { if (this.props.validate) { return this.props.validate(value, mode); } return super.isValid(value, mode); } onAfterAttach(msg) { super.onAfterAttach(msg); this.addUpdateListener(this.inputField, 'input'); } onActivateRequest(msg) { this.inputField.focus(); } handleEnter(event) { if (event.target instanceof HTMLInputElement) { return super.handleEnter(event); } return false; } }; SingleTextInputDialog = __decorate([ __param(0, (0, inversify_1.inject)(SingleTextInputDialogProps)), __metadata("design:paramtypes", [SingleTextInputDialogProps]) ], SingleTextInputDialog); exports.SingleTextInputDialog = SingleTextInputDialog; //# sourceMappingURL=dialogs.js.map