@eclipse-glsp/client
Version:
A sprotty-based client for GLSP
416 lines • 17.9 kB
JavaScript
;
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