@eclipse-glsp/client 2.2.0-next.340 → 2.2.0-next.342

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.
Files changed (64) hide show
  1. package/css/change-bounds.css +0 -9
  2. package/lib/base/mouse-position-tracker.d.ts +4 -1
  3. package/lib/base/mouse-position-tracker.d.ts.map +1 -1
  4. package/lib/base/mouse-position-tracker.js +21 -0
  5. package/lib/base/mouse-position-tracker.js.map +1 -1
  6. package/lib/features/debug/debug-manager.d.ts +2 -2
  7. package/lib/features/debug/debug-manager.d.ts.map +1 -1
  8. package/lib/features/debug/debug-manager.js +8 -6
  9. package/lib/features/debug/debug-manager.js.map +1 -1
  10. package/lib/features/element-template/mouse-tracking-element-position-listener.d.ts.map +1 -1
  11. package/lib/features/element-template/mouse-tracking-element-position-listener.js +3 -2
  12. package/lib/features/element-template/mouse-tracking-element-position-listener.js.map +1 -1
  13. package/lib/features/export/glsp-svg-exporter.d.ts +24 -2
  14. package/lib/features/export/glsp-svg-exporter.d.ts.map +1 -1
  15. package/lib/features/export/glsp-svg-exporter.js +75 -17
  16. package/lib/features/export/glsp-svg-exporter.js.map +1 -1
  17. package/lib/features/grid/grid-manager.d.ts.map +1 -1
  18. package/lib/features/grid/grid-manager.js +5 -3
  19. package/lib/features/grid/grid-manager.js.map +1 -1
  20. package/lib/features/tools/change-bounds/change-bounds-tool-move-feedback.d.ts +1 -1
  21. package/lib/features/tools/change-bounds/change-bounds-tool-move-feedback.d.ts.map +1 -1
  22. package/lib/features/tools/change-bounds/change-bounds-tool-move-feedback.js +3 -6
  23. package/lib/features/tools/change-bounds/change-bounds-tool-move-feedback.js.map +1 -1
  24. package/lib/features/tools/change-bounds/change-bounds-tool.d.ts +3 -1
  25. package/lib/features/tools/change-bounds/change-bounds-tool.d.ts.map +1 -1
  26. package/lib/features/tools/change-bounds/change-bounds-tool.js +10 -12
  27. package/lib/features/tools/change-bounds/change-bounds-tool.js.map +1 -1
  28. package/lib/features/tools/edge-creation/edge-creation-tool.d.ts +3 -0
  29. package/lib/features/tools/edge-creation/edge-creation-tool.d.ts.map +1 -1
  30. package/lib/features/tools/edge-creation/edge-creation-tool.js +14 -4
  31. package/lib/features/tools/edge-creation/edge-creation-tool.js.map +1 -1
  32. package/lib/features/tools/edge-edit/edge-edit-tool-feedback.d.ts +6 -3
  33. package/lib/features/tools/edge-edit/edge-edit-tool-feedback.d.ts.map +1 -1
  34. package/lib/features/tools/edge-edit/edge-edit-tool-feedback.js +20 -10
  35. package/lib/features/tools/edge-edit/edge-edit-tool-feedback.js.map +1 -1
  36. package/lib/features/tools/edge-edit/edge-edit-tool.d.ts +1 -0
  37. package/lib/features/tools/edge-edit/edge-edit-tool.d.ts.map +1 -1
  38. package/lib/features/tools/edge-edit/edge-edit-tool.js +5 -3
  39. package/lib/features/tools/edge-edit/edge-edit-tool.js.map +1 -1
  40. package/lib/features/tools/node-creation/container-manager.d.ts +7 -6
  41. package/lib/features/tools/node-creation/container-manager.d.ts.map +1 -1
  42. package/lib/features/tools/node-creation/container-manager.js +1 -1
  43. package/lib/features/tools/node-creation/container-manager.js.map +1 -1
  44. package/lib/features/tools/node-creation/insert-indicator.d.ts.map +1 -1
  45. package/lib/features/tools/node-creation/insert-indicator.js +1 -1
  46. package/lib/features/tools/node-creation/insert-indicator.js.map +1 -1
  47. package/lib/features/tools/node-creation/node-creation-tool.d.ts +3 -2
  48. package/lib/features/tools/node-creation/node-creation-tool.d.ts.map +1 -1
  49. package/lib/features/tools/node-creation/node-creation-tool.js +13 -4
  50. package/lib/features/tools/node-creation/node-creation-tool.js.map +1 -1
  51. package/package.json +3 -3
  52. package/src/base/mouse-position-tracker.ts +25 -2
  53. package/src/features/debug/debug-manager.ts +9 -7
  54. package/src/features/element-template/mouse-tracking-element-position-listener.ts +3 -2
  55. package/src/features/export/glsp-svg-exporter.ts +87 -18
  56. package/src/features/grid/grid-manager.ts +5 -3
  57. package/src/features/tools/change-bounds/change-bounds-tool-move-feedback.ts +3 -5
  58. package/src/features/tools/change-bounds/change-bounds-tool.ts +10 -12
  59. package/src/features/tools/edge-creation/edge-creation-tool.ts +17 -9
  60. package/src/features/tools/edge-edit/edge-edit-tool-feedback.ts +22 -9
  61. package/src/features/tools/edge-edit/edge-edit-tool.ts +6 -2
  62. package/src/features/tools/node-creation/container-manager.ts +9 -7
  63. package/src/features/tools/node-creation/insert-indicator.ts +1 -1
  64. package/src/features/tools/node-creation/node-creation-tool.ts +24 -4
@@ -14,12 +14,35 @@
14
14
  *
15
15
  * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
16
16
  ********************************************************************************/
17
- import { MousePositionTracker } from '@eclipse-glsp/sprotty';
18
- import { injectable } from 'inversify';
17
+ import {
18
+ Action,
19
+ DOMHelper,
20
+ GModelElement,
21
+ MousePositionTracker,
22
+ Point,
23
+ TYPES,
24
+ ViewerOptions,
25
+ getAbsoluteClientBounds
26
+ } from '@eclipse-glsp/sprotty';
27
+ import { inject, injectable } from 'inversify';
19
28
  import { Ranked } from './ranked';
20
29
 
21
30
  @injectable()
22
31
  export class GLSPMousePositionTracker extends MousePositionTracker implements Ranked {
23
32
  /* we want to be executed before all default mouse listeners since we are just tracking the position and others may need it */
24
33
  rank = Ranked.DEFAULT_RANK - 200;
34
+
35
+ @inject(TYPES.ViewerOptions) protected viewerOptions: ViewerOptions;
36
+ @inject(TYPES.DOMHelper) protected domHelper: DOMHelper;
37
+
38
+ override mouseMove(target: GModelElement, event: MouseEvent): (Action | Promise<Action>)[] {
39
+ // we cannot simply use the offsetX and offsetY properties of the event since they also consider nested HTML elements
40
+ // such as foreignObjects or the projection bars divs. Instead, we manually translate the client coordinates to the diagram
41
+ const clientToRoot = getAbsoluteClientBounds(target.root, this.domHelper, this.viewerOptions);
42
+ const clientToPosition = { x: event.clientX, y: event.clientY };
43
+ const rootToPosition = Point.subtract(clientToPosition, clientToRoot);
44
+ const positionOnDiagram = target.root.parentToLocal(rootToPosition);
45
+ this.lastPosition = positionOnDiagram;
46
+ return [];
47
+ }
25
48
  }
@@ -32,19 +32,21 @@ export class DebugManager implements IActionHandler {
32
32
  return this._debugEnabled;
33
33
  }
34
34
 
35
- handle(action: EnableDebugModeAction): void {
36
- this._debugEnabled = action.enable;
37
- }
38
-
39
35
  @postConstruct()
40
36
  protected init(): void {
41
37
  this.debugFeedback = this.feedbackDispatcher.createEmitter();
42
38
  }
43
39
 
44
- setDebugEnabled(visible: boolean): void {
45
- if (!visible) {
40
+ handle(action: EnableDebugModeAction): void {
41
+ this.setDebugEnabled(action.enable);
42
+ }
43
+
44
+ setDebugEnabled(enabled: boolean): void {
45
+ if (this._debugEnabled && !enabled) {
46
+ this._debugEnabled = false;
46
47
  this.debugFeedback.dispose();
47
- } else {
48
+ } else if (!this._debugEnabled && enabled) {
49
+ this._debugEnabled = true;
48
50
  this.debugFeedback
49
51
  .add(EnableDebugModeAction.create({ enable: true }), EnableDebugModeAction.create({ enable: false }))
50
52
  .submit();
@@ -54,10 +54,11 @@ export class MouseTrackingElementPositionListener extends DragAwareMouseListener
54
54
  if (!element) {
55
55
  return [];
56
56
  }
57
- if (!this.tracker.isTracking()) {
57
+ const isInitializing = !this.tracker.isTracking();
58
+ if (isInitializing) {
58
59
  this.initialize(element, ctx, event);
59
60
  }
60
- const move = this.tracker.moveElements([element], { snap: event, restrict: event });
61
+ const move = this.tracker.moveElements([element], { snap: event, restrict: event, skipStatic: !isInitializing });
61
62
  const elementMove = move.elementMoves[0];
62
63
  if (!elementMove) {
63
64
  return [];
@@ -13,38 +13,107 @@
13
13
  *
14
14
  * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
15
  ********************************************************************************/
16
+ import { ExportSvgAction, GModelRoot, RequestAction, SvgExporter } from '@eclipse-glsp/sprotty';
16
17
  import { injectable } from 'inversify';
17
18
  import { v4 as uuid } from 'uuid';
18
- import { ExportSvgAction, RequestAction, GModelRoot, SvgExporter } from '@eclipse-glsp/sprotty';
19
19
 
20
20
  @injectable()
21
21
  export class GLSPSvgExporter extends SvgExporter {
22
- override export(root: GModelRoot, _request?: RequestAction<ExportSvgAction>): void {
22
+ override export(root: GModelRoot, request?: RequestAction<ExportSvgAction>): void {
23
23
  if (typeof document !== 'undefined') {
24
- const svgElement = this.findSvgElement();
24
+ let svgElement = this.findSvgElement();
25
25
  if (svgElement) {
26
- // createSvg requires the svg to have a non-empty id, so we generate one if necessary
27
- const originalId = svgElement.id;
28
- try {
29
- svgElement.id = originalId || uuid();
30
- // provide generated svg code with respective sizing for proper viewing in browser and remove undesired border
31
- const bounds = this.getBounds(root);
32
- const svg = this.createSvg(svgElement, root).replace(
33
- 'style="',
34
- `style="width: ${bounds.width}px !important;height: ${bounds.height}px !important;border: none !important;`
35
- );
36
- // do not give request/response id here as otherwise the action is treated as an unrequested response
37
- this.actionDispatcher.dispatch(ExportSvgAction.create(svg));
38
- } finally {
39
- svgElement.id = originalId;
40
- }
26
+ svgElement = this.prepareSvgElement(svgElement, root, request);
27
+ const serializedSvg = this.createSvg(svgElement, root);
28
+ const svgExport = this.getSvgExport(serializedSvg, svgElement, root, request);
29
+ // do not give request/response id here as otherwise the action is treated as an unrequested response
30
+ this.actionDispatcher.dispatch(ExportSvgAction.create(svgExport));
41
31
  }
42
32
  }
43
33
  }
44
34
 
35
+ protected override createSvg(svgElement: SVGSVGElement, root: GModelRoot): string {
36
+ // createSvg requires the svg to have a non-empty id, so we generate one if necessary
37
+ const originalId = svgElement.id;
38
+ try {
39
+ svgElement.id = originalId || uuid();
40
+ return super.createSvg(svgElement, root);
41
+ } finally {
42
+ svgElement.id = originalId;
43
+ }
44
+ }
45
+
45
46
  protected findSvgElement(): SVGSVGElement | null {
46
47
  const div = document.getElementById(this.options.hiddenDiv);
47
48
  // search for first svg element as hierarchy within Sprotty might change
48
49
  return div && div.querySelector('svg');
49
50
  }
51
+
52
+ protected prepareSvgElement(svgElement: SVGSVGElement, root: GModelRoot, request?: RequestAction<ExportSvgAction>): SVGSVGElement {
53
+ return svgElement;
54
+ }
55
+
56
+ protected override copyStyles(source: Element, target: Element, skippedProperties: string[]): void {
57
+ this.copyStyle(source, target, skippedProperties);
58
+
59
+ // IE doesn't retrun anything on source.children
60
+ for (let i = 0; i < source.childNodes.length; ++i) {
61
+ const sourceChild = source.childNodes[i];
62
+ const targetChild = target.childNodes[i];
63
+ if (sourceChild instanceof Element) {
64
+ this.copyStyles(sourceChild, targetChild as Element, []);
65
+ }
66
+ }
67
+ }
68
+
69
+ protected copyStyle(source: Element, target: Element, skippedProperties: string[]): void {
70
+ const sourceStyle = getComputedStyle(source);
71
+ const targetStyle = getComputedStyle(target);
72
+
73
+ let style = '';
74
+ for (let i = 0; i < sourceStyle.length; i++) {
75
+ const propertyName = sourceStyle[i];
76
+ if (!skippedProperties.includes(propertyName)) {
77
+ const propertyValue = sourceStyle.getPropertyValue(propertyName);
78
+ const propertyPriority = sourceStyle.getPropertyPriority(propertyName);
79
+ if (targetStyle.getPropertyValue(propertyName) !== propertyValue) {
80
+ if (isElementCSSInlineStyle(target)) {
81
+ // rather set the property directly on the element to keep other values intact
82
+ target.style.setProperty(propertyName, propertyValue);
83
+ } else {
84
+ // collect all properties to set them at once
85
+ style += `${propertyName}: ${propertyValue}${propertyPriority ? ' !' + propertyPriority : ''}; `;
86
+ }
87
+ }
88
+ }
89
+ }
90
+ if (style !== '') {
91
+ target.setAttribute('style', style.trim());
92
+ }
93
+ }
94
+
95
+ protected getSvgExport(
96
+ serializedSvgElement: string,
97
+ svgElement: SVGElement,
98
+ root: GModelRoot,
99
+ request?: RequestAction<ExportSvgAction>
100
+ ): string {
101
+ const svgExportStyle = this.getSvgExportStyle(svgElement, root, request);
102
+ return svgExportStyle ? serializedSvgElement.replace('style="', `style="${svgExportStyle}`) : serializedSvgElement;
103
+ }
104
+
105
+ protected getSvgExportStyle(svgElement: SVGElement, root: GModelRoot, request?: RequestAction<ExportSvgAction>): string | undefined {
106
+ // provide generated svg code with respective sizing for proper viewing in browser and remove undesired border
107
+ const bounds = this.getBounds(root);
108
+ return (
109
+ `width: ${bounds.width}px !important;` +
110
+ `height: ${bounds.height}px !important;` +
111
+ 'border: none !important;' +
112
+ 'cursor: default !important;'
113
+ );
114
+ }
115
+ }
116
+
117
+ export function isElementCSSInlineStyle(element: any): element is ElementCSSInlineStyle {
118
+ return 'style' in element && element.style instanceof CSSStyleDeclaration;
50
119
  }
@@ -44,13 +44,15 @@ export class GridManager implements IActionHandler {
44
44
  }
45
45
 
46
46
  handle(action: ShowGridAction): void {
47
- this._gridVisible = action.show;
47
+ this.setGridVisible(action.show);
48
48
  }
49
49
 
50
50
  setGridVisible(visible: boolean): void {
51
- if (!visible) {
51
+ if (this._gridVisible && !visible) {
52
+ this._gridVisible = false;
52
53
  this.gridFeedback.dispose();
53
- } else {
54
+ } else if (!this._gridVisible && visible) {
55
+ this._gridVisible = true;
54
56
  this.gridFeedback.add(ShowGridAction.create({ show: true }), ShowGridAction.create({ show: false })).submit();
55
57
  }
56
58
  }
@@ -112,11 +112,9 @@ export class FeedbackMoveMouseListener extends DragAwareMouseListener {
112
112
  return [];
113
113
  }
114
114
 
115
- override mouseMove(target: GModelElement, event: MouseEvent): Action[] {
116
- super.mouseMove(target, event);
117
- if (event.buttons === 0) {
118
- return this.mouseUp(target, event);
119
- } else if (this.tracker.isTracking()) {
115
+ override draggingMouseMove(target: GModelElement, event: MouseEvent): Action[] {
116
+ super.draggingMouseMove(target, event);
117
+ if (this.tracker.isTracking()) {
120
118
  return this.moveElements(target, event);
121
119
  }
122
120
  return [];
@@ -180,7 +180,7 @@ export class ChangeBoundsListener extends DragAwareMouseListener implements ISel
180
180
  this.handleFeedback.submit();
181
181
  return true;
182
182
  } else {
183
- this.dispose();
183
+ this.disposeResize();
184
184
  return false;
185
185
  }
186
186
  }
@@ -233,21 +233,19 @@ export class ChangeBoundsListener extends DragAwareMouseListener implements ISel
233
233
  }
234
234
 
235
235
  override draggingMouseUp(target: GModelElement, event: MouseEvent): Action[] {
236
- if (!this.tracker.isTracking()) {
237
- return [];
238
- }
239
236
  const actions: Action[] = [];
240
237
  if (this.activeResizeHandle) {
241
238
  actions.push(...this.handleResizeOnServer(this.activeResizeHandle));
242
239
  } else {
240
+ // since the move feedback is handled by another class we just see whether there is something to move
243
241
  actions.push(...this.handleMoveOnServer(target));
244
242
  }
245
- this.disposeAllButHandles();
243
+ this.disposeResize({ keepHandles: true });
246
244
  return actions;
247
245
  }
248
246
 
249
247
  override nonDraggingMouseUp(element: GModelElement, event: MouseEvent): Action[] {
250
- this.disposeAllButHandles();
248
+ this.disposeResize({ keepHandles: true });
251
249
  return super.nonDraggingMouseUp(element, event);
252
250
  }
253
251
 
@@ -347,19 +345,19 @@ export class ChangeBoundsListener extends DragAwareMouseListener implements ISel
347
345
  return element !== undefined && this.activeResizeElement !== undefined && element.id === this.activeResizeElement.id;
348
346
  }
349
347
 
350
- protected disposeAllButHandles(): void {
351
- // We do not dispose the handle feedback as we want to keep showing the handles on selected elements
352
- // this.handleFeedback.dispose();
348
+ protected disposeResize(opts: { keepHandles: boolean } = { keepHandles: false }): void {
349
+ if (!opts.keepHandles) {
350
+ this.handleFeedback.dispose();
351
+ }
353
352
  this.resizeFeedback.dispose();
354
353
  this.tracker.dispose();
355
354
  this.activeResizeElement = undefined;
356
355
  this.activeResizeHandle = undefined;
357
356
  this.initialBounds = undefined;
358
- super.dispose();
359
357
  }
360
358
 
361
359
  override dispose(): void {
362
- this.handleFeedback.dispose();
363
- this.disposeAllButHandles();
360
+ this.disposeResize();
361
+ super.dispose();
364
362
  }
365
363
  }
@@ -56,24 +56,32 @@ export class EdgeCreationTool extends BaseCreationTool<TriggerEdgeCreationAction
56
56
  }
57
57
 
58
58
  doEnable(): void {
59
- const mouseMovingFeedback = new FeedbackEdgeEndMovingMouseListener(this.anchorRegistry, this.feedbackDispatcher);
59
+ this.toolFeedback();
60
+ this.creationListener();
61
+ this.trackFeedbackEdge();
62
+ }
63
+
64
+ protected toolFeedback(): void {
60
65
  const toolFeedback = this.createFeedbackEmitter()
61
66
  .add(cursorFeedbackAction(CursorCSS.OPERATION_NOT_ALLOWED), cursorFeedbackAction())
62
67
  .submit();
68
+ this.toDisposeOnDisable.push(toolFeedback);
69
+ }
70
+
71
+ protected creationListener(): void {
63
72
  const creationListener = new EdgeCreationToolMouseListener(
64
73
  this.triggerAction,
65
74
  this.actionDispatcher,
66
75
  this.typeHintProvider,
67
76
  this,
68
- this.grid?.x / 2
69
- );
70
- this.toDisposeOnDisable.push(
71
- mouseMovingFeedback,
72
- this.mouseTool.registerListener(mouseMovingFeedback),
73
- creationListener,
74
- this.mouseTool.registerListener(creationListener),
75
- toolFeedback
77
+ this.grid ? this.grid.x / 2 : undefined
76
78
  );
79
+ this.toDisposeOnDisable.push(creationListener, this.mouseTool.registerListener(creationListener));
80
+ }
81
+
82
+ protected trackFeedbackEdge(): void {
83
+ const mouseMovingFeedback = new FeedbackEdgeEndMovingMouseListener(this.anchorRegistry, this.feedbackDispatcher);
84
+ this.toDisposeOnDisable.push(mouseMovingFeedback, this.mouseTool.registerListener(mouseMovingFeedback));
77
85
  }
78
86
  }
79
87
 
@@ -41,6 +41,7 @@ import {
41
41
  typeGuard
42
42
  } from '@eclipse-glsp/sprotty';
43
43
  import { inject, injectable } from 'inversify';
44
+ import { DragAwareMouseListener } from '../../../base/drag-aware-mouse-listener';
44
45
  import { IFeedbackActionDispatcher } from '../../../base/feedback/feedback-action-dispatcher';
45
46
  import { FeedbackCommand } from '../../../base/feedback/feedback-command';
46
47
  import { FeedbackEmitter } from '../../../base/feedback/feedback-emitter';
@@ -254,7 +255,7 @@ export class FeedbackEdgeSourceMovingMouseListener extends MouseListener impleme
254
255
  }
255
256
  }
256
257
 
257
- export class FeedbackEdgeRouteMovingMouseListener extends MouseListener {
258
+ export class FeedbackEdgeRouteMovingMouseListener extends DragAwareMouseListener {
258
259
  protected tracker: ChangeBoundsTracker;
259
260
 
260
261
  constructor(
@@ -266,7 +267,7 @@ export class FeedbackEdgeRouteMovingMouseListener extends MouseListener {
266
267
  }
267
268
 
268
269
  override mouseDown(target: GModelElement, event: MouseEvent): Action[] {
269
- const result: Action[] = [];
270
+ const result = super.mouseDown(target, event);
270
271
  if (event.button === 0) {
271
272
  const routingHandle = findParentByFeature(target, isRoutingHandle);
272
273
  if (routingHandle !== undefined) {
@@ -279,11 +280,9 @@ export class FeedbackEdgeRouteMovingMouseListener extends MouseListener {
279
280
  return result;
280
281
  }
281
282
 
282
- override mouseMove(target: GModelElement, event: MouseEvent): Action[] {
283
- super.mouseMove(target, event);
284
- if (event.buttons === 0) {
285
- return this.mouseUp(target, event);
286
- } else if (this.tracker.isTracking()) {
283
+ override draggingMouseMove(target: GModelElement, event: MouseEvent): Action[] {
284
+ super.draggingMouseMove(target, event);
285
+ if (this.tracker.isTracking()) {
287
286
  return this.moveRoutingHandles(target, event);
288
287
  }
289
288
  return [];
@@ -327,10 +326,24 @@ export class FeedbackEdgeRouteMovingMouseListener extends MouseListener {
327
326
  return undefined;
328
327
  }
329
328
 
330
- override mouseUp(_target: GModelElement, _event: MouseEvent): Action[] {
331
- this.tracker.dispose();
329
+ override nonDraggingMouseUp(element: GModelElement, event: MouseEvent): Action[] {
330
+ // should reset everything that may have happend on mouse down
331
+ this.tracker.stopTracking();
332
+ return [];
333
+ }
334
+
335
+ override draggingMouseUp(_target: GModelElement, _event: MouseEvent): Action[] {
336
+ if (!this.tracker.isTracking()) {
337
+ return [];
338
+ }
339
+ this.dispose();
332
340
  return [];
333
341
  }
342
+
343
+ override dispose(): void {
344
+ this.tracker.dispose();
345
+ super.dispose();
346
+ }
334
347
  }
335
348
 
336
349
  /**
@@ -80,6 +80,7 @@ export class EdgeEditTool extends BaseEditTool {
80
80
  this.mouseTool.registerListener(this.edgeEditListener),
81
81
  this.feedbackEdgeSourceMovingListener,
82
82
  this.feedbackEdgeTargetMovingListener,
83
+ this.feedbackMovingListener,
83
84
  this.selectionService.onSelectionChanged(change => this.edgeEditListener.selectionChanged(change.root, change.selectedElements))
84
85
  );
85
86
  }
@@ -110,10 +111,12 @@ export class EdgeEditListener extends DragAwareMouseListener implements ISelecti
110
111
  // active reconnect handle data
111
112
  protected reconnectMode?: 'NEW_SOURCE' | 'NEW_TARGET';
112
113
 
114
+ protected cursorFeedback: FeedbackEmitter;
113
115
  protected editFeedback: FeedbackEmitter;
114
116
 
115
117
  constructor(protected tool: EdgeEditTool) {
116
118
  super();
119
+ this.cursorFeedback = this.tool.createFeedbackEmitter();
117
120
  this.editFeedback = this.tool.createFeedbackEmitter();
118
121
  }
119
122
 
@@ -264,11 +267,11 @@ export class EdgeEditListener extends DragAwareMouseListener implements ISelecti
264
267
  (this.reconnectMode === 'NEW_SOURCE' && currentTarget.canConnect(this.edge, 'source')) ||
265
268
  (this.reconnectMode === 'NEW_TARGET' && currentTarget.canConnect(this.edge, 'target'))
266
269
  ) {
267
- this.editFeedback.add(cursorFeedbackAction(CursorCSS.EDGE_RECONNECT), cursorFeedbackAction()).submit();
270
+ this.cursorFeedback.add(cursorFeedbackAction(CursorCSS.EDGE_RECONNECT), cursorFeedbackAction()).submit();
268
271
  return [];
269
272
  }
270
273
  }
271
- this.editFeedback.add(cursorFeedbackAction(CursorCSS.OPERATION_NOT_ALLOWED), cursorFeedbackAction()).submit();
274
+ this.cursorFeedback.add(cursorFeedbackAction(CursorCSS.OPERATION_NOT_ALLOWED), cursorFeedbackAction()).submit();
272
275
  }
273
276
  }
274
277
  return [];
@@ -307,6 +310,7 @@ export class EdgeEditListener extends DragAwareMouseListener implements ISelecti
307
310
  this.reconnectMode = undefined;
308
311
  this.newConnectable = undefined;
309
312
  this.routingHandle = undefined;
313
+ this.cursorFeedback.dispose();
310
314
  this.editFeedback.dispose();
311
315
  this.tool.deregisterFeedbackListeners();
312
316
  super.dispose();
@@ -20,12 +20,6 @@ import { CSS_GHOST_ELEMENT, CursorCSS, cursorFeedbackAction } from '../../../bas
20
20
  import { FeedbackEmitter } from '../../../base/feedback/feedback-emitter';
21
21
  import { ContainerElement, isContainable } from '../../hints/model';
22
22
  import { ChangeBoundsManager } from '../change-bounds/change-bounds-manager';
23
- export interface TrackedInsert {
24
- elementTypeId: string;
25
- location: Point;
26
- container?: GModelElement;
27
- valid: boolean;
28
- }
29
23
 
30
24
  export interface InsertOptions extends Record<string, unknown> {
31
25
  /** Flag to indicate whether the location within a container needs to be valid. Default: false */
@@ -42,6 +36,14 @@ export const DEFAULT_INSERT_OPTIONS: InsertOptions = {
42
36
  validateLocationInContainer: false
43
37
  };
44
38
 
39
+ export interface TrackedInsert {
40
+ elementTypeId: string;
41
+ location: Point;
42
+ container?: GModelElement;
43
+ valid: boolean;
44
+ options: InsertOptions;
45
+ }
46
+
45
47
  @injectable()
46
48
  export class ContainerManager {
47
49
  @inject(ChangeBoundsManager) protected readonly changeBoundsManager: ChangeBoundsManager;
@@ -54,7 +56,7 @@ export class ContainerManager {
54
56
  // we need to check whether the location is valid either because we do not have a container or the option is set
55
57
  valid = opts?.validLocationOverwrite ?? this.changeBoundsManager.hasValidPosition(proxy, location);
56
58
  }
57
- return { elementTypeId, container, location, valid };
59
+ return { elementTypeId, container, location, valid, options };
58
60
  }
59
61
 
60
62
  findContainer(location: Point, ctx: GModelElement, evt?: MouseEvent): ContainerElement | undefined {
@@ -29,7 +29,7 @@ export class InsertIndicator extends GNode implements ArgsAware {
29
29
  override type: string = InsertIndicator.TYPE;
30
30
  override features?: FeatureSet = createFeatureSet(InsertIndicator.DEFAULT_FEATURES);
31
31
  override cssClasses: string[] = ['insert-indicator', 'sprotty-node'];
32
- override size: Dimension = { width: 1, height: 1 };
32
+ override size: Dimension = Dimension.ZERO;
33
33
  args: Args = {};
34
34
 
35
35
  constructor(length?: number) {
@@ -15,7 +15,10 @@
15
15
  ********************************************************************************/
16
16
  import {
17
17
  Action,
18
+ Args,
19
+ Bounds,
18
20
  CreateNodeOperation,
21
+ Dimension,
19
22
  Disposable,
20
23
  DisposableCollection,
21
24
  GModelElement,
@@ -24,6 +27,7 @@ import {
24
27
  Point,
25
28
  TYPES,
26
29
  TriggerNodeCreationAction,
30
+ isBoundsAware,
27
31
  isCtrlOrCmd,
28
32
  isMoveable
29
33
  } from '@eclipse-glsp/sprotty';
@@ -129,16 +133,16 @@ export class NodeCreationToolMouseListener extends DragAwareMouseListener {
129
133
  return this.triggerAction.elementTypeId;
130
134
  }
131
135
 
132
- override nonDraggingMouseUp(target: GModelElement, event: MouseEvent): Action[] {
136
+ override nonDraggingMouseUp(ctx: GModelElement, event: MouseEvent): Action[] {
133
137
  const result: Action[] = [];
134
138
 
135
- const insert = this.getTrackedInsert(target, event);
139
+ const insert = this.getTrackedInsert(ctx, event);
136
140
  if (insert.valid) {
137
141
  result.push(
138
142
  CreateNodeOperation.create(this.elementTypeId, {
139
143
  location: insert.location,
140
144
  containerId: insert.container?.id,
141
- args: this.triggerAction.args
145
+ args: this.getCreateNodeOperationArgs(insert, ctx, event)
142
146
  })
143
147
  );
144
148
  }
@@ -158,6 +162,22 @@ export class NodeCreationToolMouseListener extends DragAwareMouseListener {
158
162
  return result;
159
163
  }
160
164
 
165
+ protected getCreateNodeOperationArgs(insert: TrackedInsert, ctx: GModelElement, event: MouseEvent): Args | undefined {
166
+ let args = { ...this.triggerAction.args };
167
+ const ghostElement = this.getGhostElement(ctx, event);
168
+ if (ghostElement) {
169
+ const ghostDimensions = isBoundsAware(ghostElement) ? Bounds.dimension(ghostElement.bounds) : Dimension.ZERO;
170
+ args = {
171
+ ...args,
172
+ 'ghost-x': ghostElement.position.x,
173
+ 'ghost-y': ghostElement.position.y,
174
+ 'ghost-width': ghostDimensions.width,
175
+ 'ghost-height': ghostDimensions.height
176
+ };
177
+ }
178
+ return args;
179
+ }
180
+
161
181
  protected createGhostElement(ghostElement: GhostElement): string {
162
182
  const templates = [ghostElement.template];
163
183
  this.ghostElementFeedback.add(
@@ -176,7 +196,7 @@ export class NodeCreationToolMouseListener extends DragAwareMouseListener {
176
196
  protected getTrackedInsert(ctx: GModelElement, event: MouseEvent): TrackedInsert {
177
197
  const ghostElement = this.getGhostElement(ctx, event);
178
198
  if (!ghostElement) {
179
- return { elementTypeId: this.elementTypeId, location: Point.ORIGIN, valid: false };
199
+ return { elementTypeId: this.elementTypeId, location: Point.ORIGIN, valid: false, options: { evt: event } };
180
200
  }
181
201
  return this.tool.containerManager.insert(ghostElement, ghostElement.position, this.elementTypeId, { evt: event });
182
202
  }