UNPKG

@kieler/klighd-core

Version:

Core KLighD diagram visualization with Sprotty

174 lines 9 kB
"use strict"; /* * KIELER - Kiel Integrated Environment for Layout Eclipse RichClient * * http://rtsys.informatik.uni-kiel.de/kieler * * Copyright 2021 by * + Kiel University * + Department of Computer Science * + Real-Time and Embedded Systems Group * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0. * * SPDX-License-Identifier: EPL-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); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Sidebar = void 0; /** @jsx html */ const inversify_1 = require("inversify"); const sprotty_1 = require("sprotty"); // eslint-disable-line @typescript-eslint/no-unused-vars const di_symbols_1 = require("../di.symbols"); const render_options_registry_1 = require("../options/render-options-registry"); const actions_1 = require("./actions"); const sidebar_panel_registry_1 = require("./sidebar-panel-registry"); /* global document, HTMLElement */ /** * UIExtension that adds a sidebar to the Sprotty container. The content of the * sidebar is implemented by panels, which are provided separately. The sidebar * reacts to updates of the {@link SidebarPanelRegistry} and syncs the UI with * the registry state. */ class Sidebar extends sprotty_1.AbstractUIExtension { constructor() { super(...arguments); /** * Maximum width of all opened panels. */ this.maxWidth = 0; } init() { this.actionDispatcher.dispatch(actions_1.ShowSidebarAction.create()); this.patcher = this.patcherProvider.patcher; // Update the panel if the registry state changes this.sidebarPanelRegistry.onChange(() => this.update()); // Update the panel if the current panel requests an update this.sidebarPanelRegistry.allPanels.forEach((panel) => { panel.onUpdate(() => { if (panel.id === this.sidebarPanelRegistry.currentPanelID) this.update(); }); }); } id() { return Sidebar.ID; } containerClass() { return Sidebar.ID; } update() { var _a, _b; // Only update if the content was initialized, which is the case if a // VNode Root for the panel content is created. if (!this.oldPanelContentRoot) return; const { currentPanel } = this.sidebarPanelRegistry; // Reset fit to fit content to calculate the desired width at the end. document.documentElement.style.setProperty('--sidebar-width', 'fit-content'); const content = ((0, sprotty_1.html)("div", { "class-sidebar__content": "true" }, (0, sprotty_1.html)("div", { "class-sidebar__toggle-container": "true" }, this.sidebarPanelRegistry.allPanels.map((panel) => ((0, sprotty_1.html)("button", { "class-sidebar__toggle-button": "true", "class-sidebar__toggle-button--active": this.sidebarPanelRegistry.currentPanelID === panel.id, title: panel.title, "on-click": this.handlePanelButtonClick.bind(this, panel.id) }, panel.icon)))), (0, sprotty_1.html)("h3", { "class-sidebar__title": "true" }, (_a = currentPanel === null || currentPanel === void 0 ? void 0 : currentPanel.title) !== null && _a !== void 0 ? _a : ''), (0, sprotty_1.html)("div", { "class-sidebar__panel-content": "true" }, (_b = currentPanel === null || currentPanel === void 0 ? void 0 : currentPanel.render()) !== null && _b !== void 0 ? _b : ''))); // Update panel content with efficient VDOM patching. this.oldPanelContentRoot = this.patcher(this.oldPanelContentRoot, content); // Show or hide the panel if (currentPanel) { this.containerElement.classList.add('sidebar--open'); } else { this.containerElement.classList.remove('sidebar--open'); } // Set width of sidebar to maximum width of all opened panels. this.maxWidth = Math.max(this.maxWidth, this.containerElement.clientWidth); document.documentElement.style.setProperty('--sidebar-width', `${this.maxWidth}px`); } onBeforeShow() { this.update(); } initializeContents(containerElement) { // Prepare the virtual DOM. Snabbdom requires an empty element. // Furthermore, the element is completely replaced by the panel on every update, // so we use an extra, empty element to ensure that we do not loose important attributes (such as classes). const panelContentRoot = document.createElement('div'); this.oldPanelContentRoot = this.patcher(panelContentRoot, (0, sprotty_1.html)("div", null)); containerElement.appendChild(panelContentRoot); // Notice that an AbstractUIExtension only calls initializeContents once, // so this handler is also only registered once. this.addClickOutsideListenser(containerElement); this.addMouseLeaveListener(containerElement); } handlePanelButtonClick(id) { this.actionDispatcher.dispatch(actions_1.ToggleSidebarPanelAction.create(id)); } /** * Register a click outside handler that hides the content when a user click outsides. * Using "mousedown" instead of "click" also hides the panel as soon as the user starts * dragging the diagram. */ addClickOutsideListenser(containerElement) { document.addEventListener('mousedown', (e) => { const { currentPanelID } = this.sidebarPanelRegistry; // See for information on detecting "click outside": https://stackoverflow.com/a/64665817/7569889 if (currentPanelID && !e.composedPath().includes(containerElement) && !this.renderOptionsRegistry.getValueOrDefault(render_options_registry_1.PinSidebarOption)) { this.actionDispatcher.dispatch(actions_1.ToggleSidebarPanelAction.create(currentPanelID, 'hide')); } }); } /** * Register a mouse left handler that hides the content if the mouse leaves the sidebar. */ addMouseLeaveListener(containerElement) { containerElement.addEventListener('mouseleave', (e) => { const { currentPanelID } = this.sidebarPanelRegistry; // Check if the mouse really left the bounding box of the container element. // This is necessary because the mouseleave event is also triggered when the mouse // "leaves" the sidebar to enter a tooltip. Take a small margin into account to // avoid an issue where the mouseleave event is triggered it is still inside the // container element. const rect = containerElement.getBoundingClientRect(); if (e.x <= rect.left + 2 || e.x >= rect.right - 2 || e.y <= rect.top + 2 || e.y >= rect.bottom - 2) { if (currentPanelID && !this.renderOptionsRegistry.getValueOrDefault(render_options_registry_1.PinSidebarOption)) { this.actionDispatcher.dispatch(actions_1.ToggleSidebarPanelAction.create(currentPanelID, 'hide')); } } }); } } exports.Sidebar = Sidebar; Sidebar.ID = 'sidebar'; __decorate([ (0, inversify_1.inject)(sprotty_1.TYPES.PatcherProvider), __metadata("design:type", sprotty_1.PatcherProvider) ], Sidebar.prototype, "patcherProvider", void 0); __decorate([ (0, inversify_1.inject)(sprotty_1.TYPES.IActionDispatcher), __metadata("design:type", Object) ], Sidebar.prototype, "actionDispatcher", void 0); __decorate([ (0, inversify_1.inject)(di_symbols_1.DISymbol.SidebarPanelRegistry), __metadata("design:type", sidebar_panel_registry_1.SidebarPanelRegistry) ], Sidebar.prototype, "sidebarPanelRegistry", void 0); __decorate([ (0, inversify_1.inject)(di_symbols_1.DISymbol.RenderOptionsRegistry), __metadata("design:type", render_options_registry_1.RenderOptionsRegistry) ], Sidebar.prototype, "renderOptionsRegistry", void 0); __decorate([ (0, inversify_1.postConstruct)(), __metadata("design:type", Function), __metadata("design:paramtypes", []), __metadata("design:returntype", void 0) ], Sidebar.prototype, "init", null); //# sourceMappingURL=sidebar.js.map