UNPKG

@eclipse-glsp/client

Version:

A sprotty-based client for GLSP

416 lines 17.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ROUTE_KINDS = exports.ROUTING_POINT_KINDS = exports.ALL_ROUTING_POINTS = void 0; exports.filter = filter; exports.forEachElement = forEachElement; exports.getMatchingElements = getMatchingElements; exports.getParents = getParents; exports.getChildren = getChildren; exports.getElements = getElements; exports.getSelectedElementCount = getSelectedElementCount; exports.hasSelectedElements = hasSelectedElements; exports.isNotUndefined = isNotUndefined; exports.addCssClasses = addCssClasses; exports.removeCssClasses = removeCssClasses; exports.addCssClassToElements = addCssClassToElements; exports.removeCssClassOfElements = removeCssClassOfElements; exports.toggleCssClass = toggleCssClass; exports.isNonRoutableSelectedMovableBoundsAware = isNonRoutableSelectedMovableBoundsAware; exports.isNonRoutableMovableBoundsAware = isNonRoutableMovableBoundsAware; exports.isNonRoutableSelectedBoundsAware = isNonRoutableSelectedBoundsAware; exports.isNonRoutableBoundsAware = isNonRoutableBoundsAware; exports.isRoutable = isRoutable; exports.isRoutingHandle = isRoutingHandle; exports.isSelectableAndBoundsAware = isSelectableAndBoundsAware; exports.toElementAndBounds = toElementAndBounds; exports.toElementAndRoutingPoints = toElementAndRoutingPoints; exports.calcElementAndRoutingPoints = calcElementAndRoutingPoints; exports.calcElementAndRoute = calcElementAndRoute; exports.calcRoute = calcRoute; exports.getElementTypeId = getElementTypeId; exports.findTopLevelElementByFeature = findTopLevelElementByFeature; exports.calculateDeltaBetweenPoints = calculateDeltaBetweenPoints; exports.isVisibleOnCanvas = isVisibleOnCanvas; exports.getDescendantIds = getDescendantIds; exports.isNotDescendantOfAnyElement = isNotDescendantOfAnyElement; exports.removeDescendants = removeDescendants; /******************************************************************************** * Copyright (c) 2019-2025 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 ********************************************************************************/ const sprotty_1 = require("@eclipse-glsp/sprotty"); /** * Retrieves all elements from the given {@link ModelIndexImpl} that match the given {@link ModelFilterPredicate} * @param index The {@link ModelIndexImpl}. * @param predicate The {@link ModelFilterPredicate} that should be used. * @returns A {@link FluentIterable} of all indexed element that match the predicate * (correctly casted to also include the additional type of the predicate). */ function filter(index, predicate) { return index.all().filter(predicate); } /** * Retrieves all elements from the given {@link ModelIndexImpl} that match the given {@link ModelFilterPredicate} and executes * the given runnable for each element of the result set. * @param index The {@link ModelIndexImpl}. * @param predicate The {@link ModelFilterPredicate} that should be used. * @param runnable The runnable that should be executed for each matching element. */ function forEachElement(index, predicate, runnable) { filter(index, predicate).forEach(runnable); } /** * Retrieves an array of all elements that match the given {@link ModelFilterPredicate} from the given {@link ModelIndexImpl}. * @param index The {@link ModelIndexImpl}. * @param predicate The {@link ModelFilterPredicate} that should be used. * @returns An array of all indexed element that match the predicate * (correctly casted to also include the additional type of the predicate). */ function getMatchingElements(index, predicate) { return Array.from(filter(index, predicate)); } function getParents(element, filterPredicate) { const parents = []; let current = element; while (current instanceof sprotty_1.GChildElement) { const parent = current.parent; if (filterPredicate(parent)) { parents.push(parent); } current = parent; } return parents; } function getChildren(element, filterPredicate) { const children = []; if (element instanceof sprotty_1.GParentElement) { for (const child of element.children) { if (filterPredicate(child)) { children.push(child); } if (child instanceof sprotty_1.GParentElement) { children.push(...getChildren(child, filterPredicate)); } } } return children; } /** * Invokes the given model index to retrieve the corresponding model elements for the given set of ids. Ids that * have no corresponding element in the index will be ignored. * @param index THe model index. * @param elementsIDs The element ids. * @param guard Optional typeguard. If defined only elements that match the guard will be returned. * @returns An array of the model elements that correspond to the given ids and filter predicate. */ function getElements(index, elementsIDs, guard) { // Internal filter function that filters out undefined model elements and runs an optional typeguard check. const filterFn = (element) => { if (element !== undefined) { return guard ? guard(element) : true; } return false; }; return elementsIDs.map(id => index.getById(id)).filter(filterFn); } /** * Retrieves the amount of currently selected elements in the given {@link ModelIndexImpl}. * @param index The {@link ModelIndexImpl}. * @returns The amount of selected elements. */ function getSelectedElementCount(index) { let selected = 0; forEachElement(index, sprotty_1.isSelected, element => selected++); return selected; } /** * Helper function to check wether an any element is selected in the given {@link ModelIndexImpl}. * @param index The {@link ModelIndexImpl}. * @returns `true` if at least one element is selected, `false` otherwise. */ function hasSelectedElements(index) { return getSelectedElementCount(index) > 0; } /** * Helper function to check wether an element is defined. Can be used as {@link ModelFilterPredicate}. * @param element The element that should be checked. * @returns the type predicate for `T` */ function isNotUndefined(element) { return element !== undefined; } function addCssClasses(element, ...cssClasses) { var _a; const classes = Array.isArray(cssClasses[0]) ? cssClasses[0] : cssClasses; const elementCssClasses = (_a = element.cssClasses) !== null && _a !== void 0 ? _a : []; (0, sprotty_1.distinctAdd)(elementCssClasses, ...classes); element.cssClasses = elementCssClasses; } function removeCssClasses(element, ...cssClasses) { if (!element.cssClasses || element.cssClasses.length === 0) { return; } const classes = Array.isArray(cssClasses[0]) ? cssClasses[0] : cssClasses; (0, sprotty_1.remove)(element.cssClasses, ...classes); } /** * Adds a css classs to a set of {@link GModelElement}s. * * @param elements The elements to which the css class should be added. * @param cssClass The css class to add. */ function addCssClassToElements(elements, ...cssClasses) { for (const element of elements) { addCssClasses(element, cssClasses); } } /** * Removes a css class from a set of {@link GModelElement}s. * @param elements The elements from which the css class should be removed. * @param cssClass The css class to remove. */ function removeCssClassOfElements(elements, ...cssClasses) { for (const element of elements) { removeCssClasses(element, cssClasses); } } /** * Toggles a css class on a {@link GModelElement} based on the given toggle flag. */ function toggleCssClass(element, cssClass, toggle) { return toggle ? addCssClasses(element, cssClass) : removeCssClasses(element, cssClass); } function isNonRoutableSelectedMovableBoundsAware(element) { return isNonRoutableSelectedBoundsAware(element) && (0, sprotty_1.isMoveable)(element); } function isNonRoutableMovableBoundsAware(element) { return isNonRoutableBoundsAware(element) && (0, sprotty_1.isMoveable)(element); } /** * A typeguard function to check wether a given {@link GModelElement} implements the {@link BoundsAware} model feature, * the {@link Selectable} model feature and is actually selected. In addition, the element must not be a {@link GRoutableElement}. * @param element The element to check. * @returns A type predicate indicating wether the element is of type {@link SelectableBoundsAware}. */ function isNonRoutableSelectedBoundsAware(element) { return isNonRoutableBoundsAware(element) && (0, sprotty_1.isSelected)(element); } /** * A typeguard function to check wether a given {@link GModelElement} implements the {@link BoundsAware} model feature. * In addition, the element must not be a {@link GRoutableElement}. * @param element The element to check. * @returns A type predicate indicating wether the element is of type {@link BoundsAwareModelElement}. */ function isNonRoutableBoundsAware(element) { return (0, sprotty_1.isBoundsAware)(element) && !isRoutable(element); } /** * A type guard function to check wether a given {@link GModelElement} is a {@link GRoutableElement}. * @param element The element to check. * @returns A type predicate indicating wether the element is a {@link GRoutableElement}. */ function isRoutable(element) { return element instanceof sprotty_1.GRoutableElement && element.routingPoints !== undefined; } /** * A typeguard function to check wether a given {@link GModelElement} is a {@link SRoutingHandle}. * @param element The element to check. * @returns A type predicate indicating wether the element is a {@link SRoutingHandle} */ function isRoutingHandle(element) { return element !== undefined && element instanceof sprotty_1.GRoutingHandle; } /** * A typeguard function to check wether a given {@link GModelElement} implements the {@link Selectable} model feature and * the {@link BoundsAware} model feature. * @returns A type predicate indicating wether the element is of type {@link SelectableBoundsAware}. */ function isSelectableAndBoundsAware(element) { return (0, sprotty_1.isSelectable)(element) && (0, sprotty_1.isBoundsAware)(element); } /** * Helper function to translate a given {@link GModelElement} into its corresponding {@link ElementAndBounds} representation. * @param element The element to translate. * @returns The corresponding {@link ElementAndBounds} for the given element. */ function toElementAndBounds(element) { return { elementId: element.id, newPosition: { x: element.bounds.x, y: element.bounds.y }, newSize: { width: element.bounds.width, height: element.bounds.height } }; } /** * Helper function to translate a given {@link GRoutableElement} into its corresponding * {@link ElementAndRoutingPoints} representation. * @param element The element to translate. * @returns The corresponding {@link ElementAndRoutingPoints} for the given element. */ function toElementAndRoutingPoints(element) { return { elementId: element.id, newRoutingPoints: element.routingPoints }; } /** All routing points. */ exports.ALL_ROUTING_POINTS = undefined; /** Pure routing point data kinds. */ exports.ROUTING_POINT_KINDS = ['linear', 'bezier-junction']; /** Pure route data kinds. */ exports.ROUTE_KINDS = [...exports.ROUTING_POINT_KINDS, 'source', 'target']; /** * Helper function to calculate the {@link ElementAndRoutingPoints} for a given {@link GRoutableElement}. * If client layout is activated, i.e., the edge routing registry is given and has a router for the element, then the routing * points from the calculated route are used, otherwise we use the already specified routing points of the {@link GRoutableElement}. * @param element The element to translate. * @param routerRegistry the edge router registry. * @returns The corresponding {@link ElementAndRoutingPoints} for the given element. */ function calcElementAndRoutingPoints(element, routerRegistry) { const newRoutingPoints = routerRegistry ? calcRoute(element, routerRegistry, exports.ROUTING_POINT_KINDS) : element.routingPoints; return { elementId: element.id, newRoutingPoints }; } /** * Helper function to calculate the route for a given {@link GRoutableElement}. * If client layout is activated, i.e., the edge routing registry is given and has a router for the element, then the points * from the calculated route are used, otherwise we use the already specified routing points of the {@link GRoutableElement}. * @param element The element to translate. * @param routerRegistry the edge router registry. * @returns The corresponding route for the given element. */ function calcElementAndRoute(element, routerRegistry) { var _a, _b; let route = routerRegistry ? calcRoute(element, routerRegistry, exports.ROUTE_KINDS) : undefined; if (!route) { // add source and target to the routing points route = [...element.routingPoints]; route.splice(0, 0, ((_a = element.source) === null || _a === void 0 ? void 0 : _a.position) || sprotty_1.Point.ORIGIN); route.push(((_b = element.target) === null || _b === void 0 ? void 0 : _b.position) || sprotty_1.Point.ORIGIN); } return { elementId: element.id, newRoutingPoints: route }; } /** * Helper function to calculate the route for a given {@link GRoutableElement} by filtering duplicate points. * @param element The element to translate. * @param routerRegistry the edge router registry. * @param pointKinds the routing point kinds that should be considered. * @param tolerance the tolerance applied to a point's coordinates to determine duplicates. * @returns The corresponding route for the given element. */ function calcRoute(element, routerRegistry, pointKinds = exports.ALL_ROUTING_POINTS, tolerance = Number.EPSILON) { const route = routerRegistry.get(element.routerKind).route(element); const calculatedRoute = []; for (const point of route) { // only include points we are actually interested in if (pointKinds && !pointKinds.includes(point.kind)) { continue; } // check if we are a duplicate based on coordinates in the already calculated route if (exports.ROUTING_POINT_KINDS.includes(point.kind) && calculatedRoute.find(calculatedPoint => sprotty_1.Point.maxDistance(point, calculatedPoint) < tolerance)) { continue; } calculatedRoute.push(point); } return calculatedRoute; } /** * Convenience function to retrieve the model element type from a given input. The input * can either be a {@link GModelElement}, {@link GModelElementSchema} or a string. * @param input The type input. * @returns The corresponding model type as string. */ function getElementTypeId(input) { if (typeof input === 'string') { return input; } else { return input.type; } } function findTopLevelElementByFeature(element, predicate, skip = _t => false) { let match; let current = element; while (current !== undefined) { if (!skip(current) && predicate(current)) { match = current; } if (current instanceof sprotty_1.GChildElement) { current = current.parent; } else { current = undefined; } } return match; } function calculateDeltaBetweenPoints(target, source, element) { const delta = sprotty_1.Point.subtract(target, source); const zoom = (0, sprotty_1.getZoom)(element); const adaptedDelta = sprotty_1.Point.divideScalar(delta, zoom); return adaptedDelta; } function isVisibleOnCanvas(model) { const modelBounds = (0, sprotty_1.getAbsoluteBounds)(model); const canvasBounds = model.root.canvasBounds; return (modelBounds.x <= canvasBounds.width && modelBounds.x + modelBounds.width >= 0 && modelBounds.y <= canvasBounds.height && modelBounds.y + modelBounds.height >= 0); } function getDescendantIds(element, skip) { if (!element || (skip === null || skip === void 0 ? void 0 : skip(element))) { return []; } const parent = element; const ids = [parent.id]; if (parent instanceof sprotty_1.GParentElement) { ids.push(...parent.children.flatMap(child => getDescendantIds(child, skip))); } return ids; } /** * Returns a filter function that checks if the given element is not a descendant of any of the given elements. * * @param elements The elements that the element should not be a descendant of. * @returns the filter function */ function isNotDescendantOfAnyElement(elements) { const elementsSet = new Set(elements); return (element) => { let parent = element; while (parent instanceof sprotty_1.GChildElement) { parent = parent.parent; if (elementsSet.has(parent)) { return false; } } return true; }; } /** * Removes any descendants of the given elements from the given elements. * @param elements The elements to filter. * @returns the filtered elements */ function removeDescendants(elements) { return elements.filter(isNotDescendantOfAnyElement(elements)); } //# sourceMappingURL=gmodel-util.js.map