UNPKG

@eclipse-glsp/client

Version:

A sprotty-based client for GLSP

127 lines (108 loc) 4.76 kB
/******************************************************************************** * Copyright (c) 2024 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 ********************************************************************************/ import { AbstractUIExtension, DisposableCollection } from '@eclipse-glsp/sprotty'; import { injectable } from 'inversify'; export const CSS_UI_EXTENSION_CLASS = 'ui-extension'; export const CSS_HIDDEN_EXTENSION_CLASS = 'hidden'; /** * Reusable abstract base implementation for UI extensions. * Intended for UI extensions that directly interact with the DOM API to create and manage UI elements. */ @injectable() export abstract class GLSPAbstractUIExtension extends AbstractUIExtension { protected toDisposeOnHide = new DisposableCollection(); protected get diagramContainerId(): string { return this.options.baseDiv; } protected get parentContainerSelector(): string { return '#' + this.diagramContainerId; } protected get containerSelector(): string { return '#' + this.id(); } protected get initialized(): boolean { return !!this.containerElement; } protected override initialize(): boolean { if (this.initialized) { return true; } try { this.containerElement = this.getOrCreateContainer(); this.initializeContainer(this.containerElement); this.initializeContents(this.containerElement); } catch (error) { const msg = error instanceof Error ? error.message : `Could not retrieve container element for UI extension ${this.id}`; this.logger.error(this, msg); return false; } return true; } protected override getOrCreateContainer(): HTMLElement { if (this.containerElement) { return this.containerElement; } // check if the container already exists, independent from any potential parent container // this allows us to use existing elements defined anywhere in the document const existingContainer = this.getContainer(); if (existingContainer) { return existingContainer; } // to create a container the parent container const parent = this.getParentContainer(); if (!parent?.isConnected) { throw new Error(`Could not obtain attached parent for initializing UI extension ${this.id}`); } const container = this.createContainer(parent); this.insertContainerIntoParent(container, parent); return container; } protected getContainer(): HTMLElement | null { return document.querySelector<HTMLElement>(this.containerSelector); } protected createContainer(parent: HTMLElement): HTMLElement { const container = document.createElement('div'); container.id = parent.id + '_' + this.id(); return container; } protected initializeContainer(container: HTMLElement): void { container.classList.add(CSS_UI_EXTENSION_CLASS, this.containerClass()); } protected getParentContainer(): HTMLElement | null { return document.querySelector<HTMLElement>(this.parentContainerSelector)!; } protected insertContainerIntoParent(container: HTMLElement, parent: HTMLElement): void { parent.insertBefore(container, parent.firstChild); } protected override setContainerVisible(visible: boolean): void { // the parent class simply sets the style directly, however classes provide more fine-grained control if (visible) { this.containerElement?.classList.remove(CSS_HIDDEN_EXTENSION_CLASS); } else { this.containerElement?.classList.add(CSS_HIDDEN_EXTENSION_CLASS); } } protected isContainerVisible(): boolean { return !this.containerElement?.classList.contains(CSS_HIDDEN_EXTENSION_CLASS); } protected toggleContainerVisible(): void { this.setContainerVisible(!this.isContainerVisible()); } override hide(): void { super.hide(); this.toDisposeOnHide.dispose(); } }