@eclipse-glsp/client 2.3.0-next.317 → 2.3.0-next.319
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/css/ghost-element.css +3 -1
- package/css/glsp-sprotty.css +0 -5
- package/lib/base/action-handler-registry.d.ts +10 -1
- package/lib/base/action-handler-registry.d.ts.map +1 -1
- package/lib/base/action-handler-registry.js +40 -2
- package/lib/base/action-handler-registry.js.map +1 -1
- package/lib/base/default.module.d.ts.map +1 -1
- package/lib/base/default.module.js +5 -0
- package/lib/base/default.module.js.map +1 -1
- package/lib/base/feedback/feedback-action-dispatcher.d.ts +28 -1
- package/lib/base/feedback/feedback-action-dispatcher.d.ts.map +1 -1
- package/lib/base/feedback/feedback-action-dispatcher.js +23 -1
- package/lib/base/feedback/feedback-action-dispatcher.js.map +1 -1
- package/lib/base/feedback/feedback-command.d.ts +8 -3
- package/lib/base/feedback/feedback-command.d.ts.map +1 -1
- package/lib/base/feedback/feedback-command.js +13 -2
- package/lib/base/feedback/feedback-command.js.map +1 -1
- package/lib/base/feedback/index.d.ts +1 -0
- package/lib/base/feedback/index.d.ts.map +1 -1
- package/lib/base/feedback/index.js +1 -0
- package/lib/base/feedback/index.js.map +1 -1
- package/lib/base/feedback/set-model-command.d.ts +25 -0
- package/lib/base/feedback/set-model-command.d.ts.map +1 -0
- package/lib/base/feedback/set-model-command.js +65 -0
- package/lib/base/feedback/set-model-command.js.map +1 -0
- package/lib/base/feedback/update-model-command.d.ts +17 -6
- package/lib/base/feedback/update-model-command.d.ts.map +1 -1
- package/lib/base/feedback/update-model-command.js +8 -28
- package/lib/base/feedback/update-model-command.js.map +1 -1
- package/lib/base/ranked.d.ts +10 -0
- package/lib/base/ranked.d.ts.map +1 -1
- package/lib/base/ranked.js +6 -0
- package/lib/base/ranked.js.map +1 -1
- package/lib/base/selection-service.spec.js +4 -0
- package/lib/base/selection-service.spec.js.map +1 -1
- package/lib/features/bounds/glsp-hidden-bounds-updater.d.ts +5 -3
- package/lib/features/bounds/glsp-hidden-bounds-updater.d.ts.map +1 -1
- package/lib/features/bounds/glsp-hidden-bounds-updater.js +19 -2
- package/lib/features/bounds/glsp-hidden-bounds-updater.js.map +1 -1
- package/lib/features/bounds/local-bounds.d.ts +7 -3
- package/lib/features/bounds/local-bounds.d.ts.map +1 -1
- package/lib/features/bounds/local-bounds.js +8 -4
- package/lib/features/bounds/local-bounds.js.map +1 -1
- package/lib/features/bounds/set-bounds-feedback-command.d.ts +1 -1
- package/lib/features/bounds/set-bounds-feedback-command.d.ts.map +1 -1
- package/lib/features/bounds/set-bounds-feedback-command.js +4 -2
- package/lib/features/bounds/set-bounds-feedback-command.js.map +1 -1
- package/lib/features/element-template/add-template-element.d.ts.map +1 -1
- package/lib/features/element-template/add-template-element.js +5 -4
- package/lib/features/element-template/add-template-element.js.map +1 -1
- package/lib/features/hints/type-hint-provider.d.ts +17 -2
- package/lib/features/hints/type-hint-provider.d.ts.map +1 -1
- package/lib/features/hints/type-hint-provider.js +12 -12
- package/lib/features/hints/type-hint-provider.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/utils/contribution-provider.d.ts +27 -0
- package/lib/utils/contribution-provider.d.ts.map +1 -0
- package/lib/utils/contribution-provider.js +56 -0
- package/lib/utils/contribution-provider.js.map +1 -0
- package/lib/utils/gmodel-util.d.ts +1 -0
- package/lib/utils/gmodel-util.d.ts.map +1 -1
- package/lib/utils/gmodel-util.js +14 -4
- package/lib/utils/gmodel-util.js.map +1 -1
- package/package.json +3 -3
- package/src/base/action-handler-registry.ts +41 -2
- package/src/base/default.module.ts +7 -0
- package/src/base/feedback/feedback-action-dispatcher.ts +49 -1
- package/src/base/feedback/feedback-command.ts +15 -4
- package/src/base/feedback/index.ts +2 -0
- package/src/base/feedback/set-model-command.ts +45 -0
- package/src/base/feedback/update-model-command.ts +8 -45
- package/src/base/ranked.ts +14 -0
- package/src/base/selection-service.spec.ts +7 -1
- package/src/features/bounds/glsp-hidden-bounds-updater.ts +26 -4
- package/src/features/bounds/local-bounds.ts +23 -6
- package/src/features/bounds/set-bounds-feedback-command.ts +4 -2
- package/src/features/element-template/add-template-element.ts +5 -4
- package/src/features/hints/type-hint-provider.ts +5 -5
- package/src/utils/contribution-provider.ts +65 -0
- package/src/utils/gmodel-util.ts +15 -4
|
@@ -16,20 +16,21 @@
|
|
|
16
16
|
|
|
17
17
|
import {
|
|
18
18
|
Action,
|
|
19
|
+
BoundsData,
|
|
19
20
|
ComputedBoundsAction,
|
|
20
21
|
Deferred,
|
|
21
22
|
EdgeRouterRegistry,
|
|
22
23
|
ElementAndRoutingPoints,
|
|
23
24
|
GModelElement,
|
|
24
|
-
GRoutableElement,
|
|
25
25
|
HiddenBoundsUpdater,
|
|
26
26
|
IActionDispatcher,
|
|
27
|
+
ModelIndexImpl,
|
|
27
28
|
RequestAction,
|
|
28
29
|
ResponseAction
|
|
29
30
|
} from '@eclipse-glsp/sprotty';
|
|
30
31
|
import { inject, injectable, optional } from 'inversify';
|
|
31
32
|
import { VNode } from 'snabbdom';
|
|
32
|
-
import { calcElementAndRoute, isRoutable } from '../../utils/gmodel-util';
|
|
33
|
+
import { BoundsAwareModelElement, calcElementAndRoute, getDescendantIds, isRoutable } from '../../utils/gmodel-util';
|
|
33
34
|
import { LocalComputedBoundsAction, LocalRequestBoundsAction } from './local-bounds';
|
|
34
35
|
|
|
35
36
|
/**
|
|
@@ -42,8 +43,10 @@ export class GLSPHiddenBoundsUpdater extends HiddenBoundsUpdater {
|
|
|
42
43
|
@inject(EdgeRouterRegistry) @optional() protected readonly edgeRouterRegistry?: EdgeRouterRegistry;
|
|
43
44
|
|
|
44
45
|
protected element2route: ElementAndRoutingPoints[] = [];
|
|
45
|
-
|
|
46
|
-
protected
|
|
46
|
+
|
|
47
|
+
protected getElement2BoundsData(): Map<BoundsAwareModelElement, BoundsData> {
|
|
48
|
+
return this['element2boundsData'];
|
|
49
|
+
}
|
|
47
50
|
|
|
48
51
|
override decorate(vnode: VNode, element: GModelElement): VNode {
|
|
49
52
|
super.decorate(vnode, element);
|
|
@@ -54,6 +57,9 @@ export class GLSPHiddenBoundsUpdater extends HiddenBoundsUpdater {
|
|
|
54
57
|
}
|
|
55
58
|
|
|
56
59
|
override postUpdate(cause?: Action): void {
|
|
60
|
+
if (LocalRequestBoundsAction.is(cause) && cause.elementIDs) {
|
|
61
|
+
this.focusOnElements(cause.elementIDs);
|
|
62
|
+
}
|
|
57
63
|
const actions = this.captureActions(() => super.postUpdate(cause));
|
|
58
64
|
actions
|
|
59
65
|
.filter(action => ComputedBoundsAction.is(action))
|
|
@@ -61,6 +67,22 @@ export class GLSPHiddenBoundsUpdater extends HiddenBoundsUpdater {
|
|
|
61
67
|
this.element2route = [];
|
|
62
68
|
}
|
|
63
69
|
|
|
70
|
+
protected focusOnElements(elementIDs: string[]): void {
|
|
71
|
+
const data = this.getElement2BoundsData();
|
|
72
|
+
if (data.size > 0) {
|
|
73
|
+
// expand given IDs to their descendent element IDs as we need their bounding boxes as well
|
|
74
|
+
const index = [...data.keys()][0].index;
|
|
75
|
+
const relevantIds = new Set(elementIDs.flatMap(elementId => this.expandElementId(elementId, index, elementIDs)));
|
|
76
|
+
|
|
77
|
+
// ensure we only keep the bounds of the elements we are interested in
|
|
78
|
+
data.forEach((_bounds, element) => !relevantIds.has(element.id) && data.delete(element));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
protected expandElementId(id: string, index: ModelIndexImpl, elementIDs: string[]): string[] {
|
|
83
|
+
return getDescendantIds(index.getById(id));
|
|
84
|
+
}
|
|
85
|
+
|
|
64
86
|
protected captureActions(call: () => void): Action[] {
|
|
65
87
|
const capturingActionDispatcher = new CapturingActionDispatcher();
|
|
66
88
|
const actualActionDispatcher = this.actionDispatcher;
|
|
@@ -26,21 +26,38 @@ import {
|
|
|
26
26
|
GModelRootSchema,
|
|
27
27
|
RequestBoundsAction,
|
|
28
28
|
TYPES,
|
|
29
|
-
ViewerOptions
|
|
29
|
+
ViewerOptions,
|
|
30
|
+
hasArrayProp
|
|
30
31
|
} from '@eclipse-glsp/sprotty';
|
|
31
32
|
import { inject, injectable } from 'inversify';
|
|
32
33
|
import { ServerAction } from '../../base/model/glsp-model-source';
|
|
33
34
|
|
|
35
|
+
export interface LocalRequestBoundsAction extends RequestBoundsAction {
|
|
36
|
+
elementIDs?: string[];
|
|
37
|
+
}
|
|
38
|
+
|
|
34
39
|
export namespace LocalRequestBoundsAction {
|
|
35
|
-
export function is(object: unknown): object is
|
|
36
|
-
return RequestBoundsAction.is(object) && !ServerAction.is(object);
|
|
40
|
+
export function is(object: unknown): object is LocalRequestBoundsAction {
|
|
41
|
+
return RequestBoundsAction.is(object) && !ServerAction.is(object) && hasArrayProp(object, 'elementIDs', true);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function create(newRoot: GModelRootSchema, elementIDs?: string[]): LocalRequestBoundsAction {
|
|
45
|
+
return {
|
|
46
|
+
...RequestBoundsAction.create(newRoot),
|
|
47
|
+
elementIDs
|
|
48
|
+
};
|
|
37
49
|
}
|
|
38
50
|
|
|
39
|
-
export function fromCommand(
|
|
51
|
+
export function fromCommand(
|
|
52
|
+
{ root }: CommandExecutionContext,
|
|
53
|
+
actionDispatcher: ActionDispatcher,
|
|
54
|
+
cause?: Action,
|
|
55
|
+
elementIDs?: string[]
|
|
56
|
+
): CommandResult {
|
|
40
57
|
// do not modify the main model (modelChanged = false) but request local bounds calculation on hidden model
|
|
41
|
-
actionDispatcher.dispatch(
|
|
58
|
+
actionDispatcher.dispatch(LocalRequestBoundsAction.create(root as unknown as GModelRootSchema, elementIDs));
|
|
42
59
|
return {
|
|
43
|
-
model:
|
|
60
|
+
model: root,
|
|
44
61
|
modelChanged: false,
|
|
45
62
|
cause
|
|
46
63
|
};
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
isLayoutContainer
|
|
25
25
|
} from '@eclipse-glsp/sprotty';
|
|
26
26
|
import { inject, injectable } from 'inversify';
|
|
27
|
+
import { Ranked } from '../../base';
|
|
27
28
|
import { GLSPActionDispatcher } from '../../base/action-dispatcher';
|
|
28
29
|
import { FeedbackCommand } from '../../base/feedback/feedback-command';
|
|
29
30
|
import { LocalRequestBoundsAction } from './local-bounds';
|
|
@@ -48,7 +49,7 @@ export namespace SetBoundsFeedbackAction {
|
|
|
48
49
|
export class SetBoundsFeedbackCommand extends SetBoundsCommand implements FeedbackCommand {
|
|
49
50
|
static override readonly KIND: string = SetBoundsFeedbackAction.KIND;
|
|
50
51
|
|
|
51
|
-
readonly
|
|
52
|
+
readonly rank: number = Ranked.DEFAULT_RANK;
|
|
52
53
|
|
|
53
54
|
@inject(TYPES.IActionDispatcher) protected actionDispatcher: GLSPActionDispatcher;
|
|
54
55
|
|
|
@@ -65,6 +66,7 @@ export class SetBoundsFeedbackCommand extends SetBoundsCommand implements Feedba
|
|
|
65
66
|
element.layoutOptions = options;
|
|
66
67
|
}
|
|
67
68
|
});
|
|
68
|
-
|
|
69
|
+
const elementIDs = this.action.bounds.map(bounds => bounds.elementId);
|
|
70
|
+
return LocalRequestBoundsAction.fromCommand(context, this.actionDispatcher, this.action, elementIDs);
|
|
69
71
|
}
|
|
70
72
|
}
|
|
@@ -64,13 +64,14 @@ export class AddTemplateElementsFeedbackCommand extends FeedbackCommand {
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
override execute(context: CommandExecutionContext): CommandResult {
|
|
67
|
-
this.action.templates
|
|
67
|
+
const templateElements = this.action.templates
|
|
68
68
|
.map(template => templateToSchema(template, context))
|
|
69
69
|
.filter(isNotUndefined)
|
|
70
70
|
.map(schema => context.modelFactory.createElement(schema))
|
|
71
|
-
.map(element => this.applyRootCssClasses(element, this.action.addClasses, this.action.removeClasses))
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
.map(element => this.applyRootCssClasses(element, this.action.addClasses, this.action.removeClasses));
|
|
72
|
+
templateElements.forEach(templateElement => context.root.add(templateElement));
|
|
73
|
+
const templateElementIDs = templateElements.map(element => element.id);
|
|
74
|
+
return LocalRequestBoundsAction.fromCommand(context, this.actionDispatcher, this.action, templateElementIDs);
|
|
74
75
|
}
|
|
75
76
|
|
|
76
77
|
protected applyRootCssClasses(element: GChildElement, addClasses?: string[], removeClasses?: string[]): GChildElement {
|
|
@@ -13,19 +13,18 @@
|
|
|
13
13
|
*
|
|
14
14
|
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
|
|
15
15
|
********************************************************************************/
|
|
16
|
-
import { inject, injectable } from 'inversify';
|
|
17
16
|
import {
|
|
18
17
|
Action,
|
|
19
18
|
CommandExecutionContext,
|
|
20
19
|
Connectable,
|
|
21
20
|
EdgeTypeHint,
|
|
22
|
-
IActionHandler,
|
|
23
|
-
RequestTypeHintsAction,
|
|
24
21
|
GModelElement,
|
|
25
22
|
GModelElementSchema,
|
|
26
23
|
GModelRoot,
|
|
27
24
|
GRoutableElement,
|
|
28
25
|
GShapeElement,
|
|
26
|
+
IActionHandler,
|
|
27
|
+
RequestTypeHintsAction,
|
|
29
28
|
SetTypeHintsAction,
|
|
30
29
|
ShapeTypeHint,
|
|
31
30
|
TYPES,
|
|
@@ -36,16 +35,17 @@ import {
|
|
|
36
35
|
isConnectable,
|
|
37
36
|
moveFeature
|
|
38
37
|
} from '@eclipse-glsp/sprotty';
|
|
38
|
+
import { inject, injectable } from 'inversify';
|
|
39
39
|
|
|
40
40
|
import { GLSPActionDispatcher } from '../../base/action-dispatcher';
|
|
41
41
|
import { IFeedbackActionDispatcher } from '../../base/feedback/feedback-action-dispatcher';
|
|
42
42
|
import { FeedbackCommand } from '../../base/feedback/feedback-command';
|
|
43
43
|
import { IDiagramStartup } from '../../base/model/diagram-loader';
|
|
44
|
+
import { GEdge } from '../../model';
|
|
44
45
|
import { getElementTypeId } from '../../utils/gmodel-util';
|
|
45
46
|
import { resizeFeature } from '../change-bounds/model';
|
|
46
47
|
import { reconnectFeature } from '../reconnect/model';
|
|
47
48
|
import { containerFeature, isContainable, reparentFeature } from './model';
|
|
48
|
-
import { GEdge } from '../../model';
|
|
49
49
|
|
|
50
50
|
/**
|
|
51
51
|
* Is dispatched by the {@link TypeHintProvider} to apply the type hints received from the server
|
|
@@ -78,7 +78,7 @@ type CanConnectFn = Connectable['canConnect'];
|
|
|
78
78
|
@injectable()
|
|
79
79
|
export class ApplyTypeHintsCommand extends FeedbackCommand {
|
|
80
80
|
public static KIND = ApplyTypeHintsAction.KIND;
|
|
81
|
-
public override readonly
|
|
81
|
+
public override readonly rank: number = -10;
|
|
82
82
|
|
|
83
83
|
@inject(TYPES.ITypeHintProvider)
|
|
84
84
|
protected typeHintProvider: ITypeHintProvider;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/*******************************************************************************
|
|
2
|
+
* Copyright (C) 2017 TypeFox and others.
|
|
3
|
+
* Modifications: (c) 2024 EclipseSource and others.
|
|
4
|
+
*
|
|
5
|
+
* This program and the accompanying materials are made available under the
|
|
6
|
+
* terms of the Eclipse Public License v. 2.0 which is available at
|
|
7
|
+
* http://www.eclipse.org/legal/epl-2.0.
|
|
8
|
+
*
|
|
9
|
+
* This Source Code may also be made available under the following Secondary
|
|
10
|
+
* Licenses when the conditions for such availability set forth in the Eclipse
|
|
11
|
+
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
12
|
+
* with the GNU Classpath Exception which is available at
|
|
13
|
+
* https://www.gnu.org/software/classpath/license.html.
|
|
14
|
+
*
|
|
15
|
+
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
16
|
+
*******************************************************************************/
|
|
17
|
+
// eslint-disable-next-line max-len
|
|
18
|
+
// based on https://github.com/eclipse-theia/theia/blob/9ff0cedff1d591b0eb4be97a05f6d992789d0a24/packages/core/src/common/contribution-provider.ts
|
|
19
|
+
|
|
20
|
+
import { BindingContext, TYPES } from '@eclipse-glsp/sprotty';
|
|
21
|
+
import { interfaces } from 'inversify';
|
|
22
|
+
|
|
23
|
+
export interface IContributionProvider<T extends object> {
|
|
24
|
+
/**
|
|
25
|
+
* @param recursive `true` if the contributions should be collected from the parent containers as well. Otherwise, `false`.
|
|
26
|
+
* It is `false` by default.
|
|
27
|
+
*/
|
|
28
|
+
getContributions(recursive?: boolean): T[];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
class ContainerBasedContributionProvider<T extends object> implements IContributionProvider<T> {
|
|
32
|
+
protected services: T[] | undefined;
|
|
33
|
+
|
|
34
|
+
constructor(
|
|
35
|
+
protected readonly serviceIdentifier: interfaces.ServiceIdentifier<T>,
|
|
36
|
+
protected readonly container: interfaces.Container
|
|
37
|
+
) {}
|
|
38
|
+
|
|
39
|
+
getContributions(recursive?: boolean): T[] {
|
|
40
|
+
if (this.services === undefined) {
|
|
41
|
+
const currentServices: T[] = [];
|
|
42
|
+
let currentContainer: interfaces.Container | undefined = this.container;
|
|
43
|
+
while (currentContainer !== undefined) {
|
|
44
|
+
if (currentContainer.isBound(this.serviceIdentifier)) {
|
|
45
|
+
try {
|
|
46
|
+
currentServices.push(...currentContainer.getAll(this.serviceIdentifier));
|
|
47
|
+
} catch (error) {
|
|
48
|
+
console.error(error);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
currentContainer = recursive === true && currentContainer.parent ? currentContainer.parent : undefined;
|
|
52
|
+
}
|
|
53
|
+
this.services = currentServices;
|
|
54
|
+
}
|
|
55
|
+
return this.services;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function bindContributionProvider(context: Pick<BindingContext, 'bind'> | interfaces.Bind, id: symbol): void {
|
|
60
|
+
const bind = typeof context === 'object' ? context.bind.bind(context) : context;
|
|
61
|
+
bind(TYPES.IContributionProvider)
|
|
62
|
+
.toDynamicValue(ctx => new ContainerBasedContributionProvider(id, ctx.container))
|
|
63
|
+
.inSingletonScope()
|
|
64
|
+
.whenTargetNamed(id);
|
|
65
|
+
}
|
package/src/utils/gmodel-util.ts
CHANGED
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
GChildElement,
|
|
23
23
|
GModelElement,
|
|
24
24
|
GModelElementSchema,
|
|
25
|
+
GParentElement,
|
|
25
26
|
GRoutableElement,
|
|
26
27
|
GRoutingHandle,
|
|
27
28
|
ModelIndexImpl,
|
|
@@ -30,13 +31,12 @@ import {
|
|
|
30
31
|
Selectable,
|
|
31
32
|
TypeGuard,
|
|
32
33
|
distinctAdd,
|
|
33
|
-
findParentByFeature,
|
|
34
34
|
getAbsoluteBounds,
|
|
35
|
+
getZoom,
|
|
35
36
|
isBoundsAware,
|
|
36
37
|
isMoveable,
|
|
37
38
|
isSelectable,
|
|
38
39
|
isSelected,
|
|
39
|
-
isViewport,
|
|
40
40
|
remove
|
|
41
41
|
} from '@eclipse-glsp/sprotty';
|
|
42
42
|
|
|
@@ -346,8 +346,7 @@ export function findTopLevelElementByFeature<T>(
|
|
|
346
346
|
|
|
347
347
|
export function calculateDeltaBetweenPoints(target: Point, source: Point, element: GModelElement): Point {
|
|
348
348
|
const delta = Point.subtract(target, source);
|
|
349
|
-
const
|
|
350
|
-
const zoom = viewport?.zoom ?? 1;
|
|
349
|
+
const zoom = getZoom(element);
|
|
351
350
|
const adaptedDelta = { x: delta.x / zoom, y: delta.y / zoom };
|
|
352
351
|
return adaptedDelta;
|
|
353
352
|
}
|
|
@@ -362,3 +361,15 @@ export function isVisibleOnCanvas(model: BoundsAwareModelElement): boolean {
|
|
|
362
361
|
modelBounds.y + modelBounds.height >= 0
|
|
363
362
|
);
|
|
364
363
|
}
|
|
364
|
+
|
|
365
|
+
export function getDescendantIds(element?: GModelElement, skip?: (t: GModelElement) => boolean): string[] {
|
|
366
|
+
if (!element || skip?.(element)) {
|
|
367
|
+
return [];
|
|
368
|
+
}
|
|
369
|
+
const parent = element;
|
|
370
|
+
const ids = [parent.id];
|
|
371
|
+
if (parent instanceof GParentElement) {
|
|
372
|
+
ids.push(...parent.children.flatMap(child => getDescendantIds(child, skip)));
|
|
373
|
+
}
|
|
374
|
+
return ids;
|
|
375
|
+
}
|