UNPKG

sprotty

Version:

A next-gen framework for graphical views

275 lines 12.5 kB
"use strict"; /******************************************************************************** * Copyright (c) 2019-2022 EclipseSource 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 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 EditLabelUI_1; Object.defineProperty(exports, "__esModule", { value: true }); exports.EditLabelUI = exports.EditLabelActionHandler = void 0; const inversify_1 = require("inversify"); const actions_1 = require("sprotty-protocol/lib/actions"); const types_1 = require("../../base/types"); const ui_extension_1 = require("../../base/ui-extensions/ui-extension"); const ui_extension_registry_1 = require("../../base/ui-extensions/ui-extension-registry"); const dom_helper_1 = require("../../base/views/dom-helper"); const commit_model_1 = require("../../model-source/commit-model"); const keyboard_1 = require("../../utils/keyboard"); const model_1 = require("../bounds/model"); const zoom_1 = require("../viewport/zoom"); const edit_label_1 = require("./edit-label"); const model_2 = require("./model"); /** Shows a UI extension for editing a label on emitted `EditLabelAction`s. */ let EditLabelActionHandler = class EditLabelActionHandler { handle(action) { if ((0, edit_label_1.isEditLabelAction)(action)) { return ui_extension_registry_1.SetUIExtensionVisibilityAction.create({ extensionId: EditLabelUI.ID, visible: true, contextElementsId: [action.labelId] }); } } }; exports.EditLabelActionHandler = EditLabelActionHandler; exports.EditLabelActionHandler = EditLabelActionHandler = __decorate([ (0, inversify_1.injectable)() ], EditLabelActionHandler); let EditLabelUI = EditLabelUI_1 = class EditLabelUI extends ui_extension_1.AbstractUIExtension { constructor() { super(...arguments); this.validationTimeout = undefined; this.isActive = false; this.blockApplyEditOnInvalidInput = true; this.isCurrentLabelValid = true; } id() { return EditLabelUI_1.ID; } containerClass() { return 'label-edit'; } get labelId() { return this.label ? this.label.id : 'unknown'; } initializeContents(containerElement) { containerElement.style.position = 'absolute'; this.inputElement = document.createElement('input'); this.textAreaElement = document.createElement('textarea'); [this.inputElement, this.textAreaElement].forEach((element) => { element.onkeydown = event => this.applyLabelEditOnEvent(event, 'Enter'); this.configureAndAdd(element, containerElement); }); } configureAndAdd(element, containerElement) { element.style.visibility = 'hidden'; element.style.position = 'absolute'; element.style.top = '0px'; element.style.left = '0px'; element.addEventListener('keydown', (event) => this.hideIfEscapeEvent(event)); element.addEventListener('keyup', (event) => this.validateLabelIfContentChange(event, element.value)); element.addEventListener('blur', () => window.setTimeout(() => this.applyLabelEdit(), 200)); containerElement.appendChild(element); } get editControl() { if (this.label && this.label.isMultiLine) { return this.textAreaElement; } return this.inputElement; } hideIfEscapeEvent(event) { if ((0, keyboard_1.matchesKeystroke)(event, 'Escape')) { this.hide(); } } applyLabelEditOnEvent(event, code, ...modifiers) { if ((0, keyboard_1.matchesKeystroke)(event, code ? code : 'Enter', ...modifiers)) { event.preventDefault(); this.applyLabelEdit(); } } validateLabelIfContentChange(event, value) { if (this.previousLabelContent === undefined || this.previousLabelContent !== value) { this.previousLabelContent = value; this.performLabelValidation(event, this.editControl.value); } } async applyLabelEdit() { var _a; if (!this.isActive) { return; } if (((_a = this.label) === null || _a === void 0 ? void 0 : _a.text) === this.editControl.value) { // no action necessary this.hide(); return; } if (this.blockApplyEditOnInvalidInput) { const result = await this.validateLabel(this.editControl.value); if ('error' === result.severity) { this.editControl.focus(); return; } } this.actionDispatcherProvider() .then((actionDispatcher) => actionDispatcher.dispatchAll([actions_1.ApplyLabelEditAction.create(this.labelId, this.editControl.value), commit_model_1.CommitModelAction.create()])) .catch((reason) => this.logger.error(this, 'No action dispatcher available to execute apply label edit action', reason)); this.hide(); } performLabelValidation(event, value) { if (this.validationTimeout) { window.clearTimeout(this.validationTimeout); } this.validationTimeout = window.setTimeout(() => this.validateLabel(value), 200); } async validateLabel(value) { if (this.labelValidator && this.label) { try { const result = await this.labelValidator.validate(value, this.label); this.isCurrentLabelValid = 'error' !== result.severity; this.showValidationResult(result); return result; } catch (reason) { this.logger.error(this, 'Error validating edited label', reason); } } this.isCurrentLabelValid = true; return { severity: 'ok', message: undefined }; } showValidationResult(result) { this.clearValidationResult(); if (this.validationDecorator) { this.validationDecorator.decorate(this.editControl, result); } } clearValidationResult() { if (this.validationDecorator) { this.validationDecorator.dispose(this.editControl); } } show(root, ...contextElementIds) { if (!hasEditableLabel(contextElementIds, root) || this.isActive) { return; } super.show(root, ...contextElementIds); this.isActive = true; } hide() { this.editControl.style.visibility = 'hidden'; super.hide(); this.clearValidationResult(); this.isActive = false; this.isCurrentLabelValid = true; this.previousLabelContent = undefined; if (this.labelElement) { this.labelElement.style.visibility = 'visible'; } } onBeforeShow(containerElement, root, ...contextElementIds) { this.label = getEditableLabels(contextElementIds, root)[0]; this.previousLabelContent = this.label.text; this.setPosition(containerElement); this.applyTextContents(); this.applyFontStyling(); this.editControl.style.visibility = 'visible'; this.editControl.focus(); } setPosition(containerElement) { let x = 0; let y = 0; let width = 100; let height = 20; if (this.label) { const zoom = (0, zoom_1.getZoom)(this.label); const bounds = (0, model_1.getAbsoluteClientBounds)(this.label, this.domHelper, this.viewerOptions); x = bounds.x + (this.label.editControlPositionCorrection ? this.label.editControlPositionCorrection.x : 0) * zoom; y = bounds.y + (this.label.editControlPositionCorrection ? this.label.editControlPositionCorrection.y : 0) * zoom; height = (this.label.editControlDimension ? this.label.editControlDimension.height : height) * zoom; width = (this.label.editControlDimension ? this.label.editControlDimension.width : width) * zoom; } containerElement.style.left = `${x}px`; containerElement.style.top = `${y}px`; containerElement.style.width = `${width}px`; this.editControl.style.width = `${width}px`; containerElement.style.height = `${height}px`; this.editControl.style.height = `${height}px`; } applyTextContents() { if (this.label) { this.editControl.value = this.label.text; if (this.editControl instanceof HTMLTextAreaElement) { this.editControl.selectionStart = 0; this.editControl.selectionEnd = 0; this.editControl.scrollTop = 0; this.editControl.scrollLeft = 0; } else { this.editControl.setSelectionRange(0, this.editControl.value.length); } } } applyFontStyling() { if (this.label) { this.labelElement = document.getElementById(this.domHelper.createUniqueDOMElementId(this.label)); if (this.labelElement) { this.labelElement.style.visibility = 'hidden'; const style = window.getComputedStyle(this.labelElement); this.editControl.style.font = style.font; this.editControl.style.fontStyle = style.fontStyle; this.editControl.style.fontFamily = style.fontFamily; this.editControl.style.fontSize = scaledFont(style.fontSize, (0, zoom_1.getZoom)(this.label)); this.editControl.style.fontWeight = style.fontWeight; this.editControl.style.lineHeight = style.lineHeight; } } } }; exports.EditLabelUI = EditLabelUI; EditLabelUI.ID = 'editLabelUi'; __decorate([ (0, inversify_1.inject)(types_1.TYPES.IActionDispatcherProvider), __metadata("design:type", Function) ], EditLabelUI.prototype, "actionDispatcherProvider", void 0); __decorate([ (0, inversify_1.inject)(types_1.TYPES.ViewerOptions), __metadata("design:type", Object) ], EditLabelUI.prototype, "viewerOptions", void 0); __decorate([ (0, inversify_1.inject)(types_1.TYPES.DOMHelper), __metadata("design:type", dom_helper_1.DOMHelper) ], EditLabelUI.prototype, "domHelper", void 0); __decorate([ (0, inversify_1.inject)(types_1.TYPES.IEditLabelValidator), (0, inversify_1.optional)(), __metadata("design:type", Object) ], EditLabelUI.prototype, "labelValidator", void 0); __decorate([ (0, inversify_1.inject)(types_1.TYPES.IEditLabelValidationDecorator), (0, inversify_1.optional)(), __metadata("design:type", Object) ], EditLabelUI.prototype, "validationDecorator", void 0); exports.EditLabelUI = EditLabelUI = EditLabelUI_1 = __decorate([ (0, inversify_1.injectable)() ], EditLabelUI); function hasEditableLabel(contextElementIds, root) { return getEditableLabels(contextElementIds, root).length === 1; } function getEditableLabels(contextElementIds, root) { return contextElementIds.map(id => root.index.getById(id)).filter(model_2.isEditableLabel); } function scaledFont(font, zoom) { return font.replace(/\d+(\.\d+)?/, (match) => { return String(Number.parseInt(match, 10) * zoom); }); } //# sourceMappingURL=edit-label-ui.js.map