@eclipse-glsp/client 2.1.0-next.295 → 2.1.0-next.296

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 (110) hide show
  1. package/css/ghost-element.css +25 -0
  2. package/css/glsp-sprotty.css +5 -0
  3. package/lib/base/args-feature.d.ts +2 -1
  4. package/lib/base/args-feature.d.ts.map +1 -1
  5. package/lib/base/args-feature.js +12 -2
  6. package/lib/base/args-feature.js.map +1 -1
  7. package/lib/base/feedback/css-feedback.d.ts +17 -0
  8. package/lib/base/feedback/css-feedback.d.ts.map +1 -1
  9. package/lib/base/feedback/css-feedback.js +4 -2
  10. package/lib/base/feedback/css-feedback.js.map +1 -1
  11. package/lib/default-modules.d.ts +1 -1
  12. package/lib/default-modules.d.ts.map +1 -1
  13. package/lib/default-modules.js +2 -0
  14. package/lib/default-modules.js.map +1 -1
  15. package/lib/features/accessibility/keyboard-tool-palette/keyboard-tool-palette-module.d.ts.map +1 -1
  16. package/lib/features/accessibility/keyboard-tool-palette/keyboard-tool-palette-module.js +4 -2
  17. package/lib/features/accessibility/keyboard-tool-palette/keyboard-tool-palette-module.js.map +1 -1
  18. package/lib/features/accessibility/keyboard-tool-palette/keyboard-tool-palette.d.ts +16 -1
  19. package/lib/features/accessibility/keyboard-tool-palette/keyboard-tool-palette.d.ts.map +1 -1
  20. package/lib/features/accessibility/keyboard-tool-palette/keyboard-tool-palette.js +9 -10
  21. package/lib/features/accessibility/keyboard-tool-palette/keyboard-tool-palette.js.map +1 -1
  22. package/lib/features/bounds/bounds-module.d.ts.map +1 -1
  23. package/lib/features/bounds/bounds-module.js +4 -0
  24. package/lib/features/bounds/bounds-module.js.map +1 -1
  25. package/lib/features/bounds/glsp-hidden-bounds-updater.d.ts +2 -2
  26. package/lib/features/bounds/glsp-hidden-bounds-updater.d.ts.map +1 -1
  27. package/lib/features/bounds/glsp-hidden-bounds-updater.js +7 -3
  28. package/lib/features/bounds/glsp-hidden-bounds-updater.js.map +1 -1
  29. package/lib/features/bounds/local-bounds.d.ts +34 -0
  30. package/lib/features/bounds/local-bounds.d.ts.map +1 -0
  31. package/lib/features/bounds/local-bounds.js +99 -0
  32. package/lib/features/bounds/local-bounds.js.map +1 -0
  33. package/lib/features/bounds/set-bounds-feedback-command.d.ts +33 -0
  34. package/lib/features/bounds/set-bounds-feedback-command.d.ts.map +1 -0
  35. package/lib/features/bounds/set-bounds-feedback-command.js +74 -0
  36. package/lib/features/bounds/set-bounds-feedback-command.js.map +1 -0
  37. package/lib/features/change-bounds/model.d.ts +1 -1
  38. package/lib/features/change-bounds/model.d.ts.map +1 -1
  39. package/lib/features/change-bounds/model.js +0 -1
  40. package/lib/features/change-bounds/model.js.map +1 -1
  41. package/lib/features/decoration/decoration-placer.d.ts +16 -1
  42. package/lib/features/decoration/decoration-placer.d.ts.map +1 -1
  43. package/lib/features/decoration/decoration-placer.js +3 -3
  44. package/lib/features/decoration/decoration-placer.js.map +1 -1
  45. package/lib/features/element-template/add-template-element.d.ts +39 -0
  46. package/lib/features/element-template/add-template-element.d.ts.map +1 -0
  47. package/lib/features/element-template/add-template-element.js +107 -0
  48. package/lib/features/element-template/add-template-element.js.map +1 -0
  49. package/lib/features/element-template/element-template-module.d.ts +18 -0
  50. package/lib/features/element-template/element-template-module.d.ts.map +1 -0
  51. package/lib/features/element-template/element-template-module.js +27 -0
  52. package/lib/features/element-template/element-template-module.js.map +1 -0
  53. package/lib/features/element-template/mouse-tracking-element-position-listener.d.ts +38 -0
  54. package/lib/features/element-template/mouse-tracking-element-position-listener.d.ts.map +1 -0
  55. package/lib/features/element-template/mouse-tracking-element-position-listener.js +102 -0
  56. package/lib/features/element-template/mouse-tracking-element-position-listener.js.map +1 -0
  57. package/lib/features/element-template/remove-template-element.d.ts +32 -0
  58. package/lib/features/element-template/remove-template-element.d.ts.map +1 -0
  59. package/lib/features/element-template/remove-template-element.js +66 -0
  60. package/lib/features/element-template/remove-template-element.js.map +1 -0
  61. package/lib/features/tool-palette/tool-palette-module.d.ts.map +1 -1
  62. package/lib/features/tool-palette/tool-palette-module.js +2 -0
  63. package/lib/features/tool-palette/tool-palette-module.js.map +1 -1
  64. package/lib/features/tool-palette/tool-palette.d.ts +22 -4
  65. package/lib/features/tool-palette/tool-palette.d.ts.map +1 -1
  66. package/lib/features/tool-palette/tool-palette.js +35 -11
  67. package/lib/features/tool-palette/tool-palette.js.map +1 -1
  68. package/lib/features/tools/change-bounds/change-bounds-tool.d.ts +1 -1
  69. package/lib/features/tools/change-bounds/change-bounds-tool.d.ts.map +1 -1
  70. package/lib/features/tools/change-bounds/change-bounds-tool.js +6 -5
  71. package/lib/features/tools/change-bounds/change-bounds-tool.js.map +1 -1
  72. package/lib/features/tools/change-bounds/view.d.ts +16 -1
  73. package/lib/features/tools/change-bounds/view.d.ts.map +1 -1
  74. package/lib/features/tools/change-bounds/view.js +1 -1
  75. package/lib/features/tools/change-bounds/view.js.map +1 -1
  76. package/lib/features/tools/node-creation/node-creation-module.d.ts.map +1 -1
  77. package/lib/features/tools/node-creation/node-creation-module.js +2 -1
  78. package/lib/features/tools/node-creation/node-creation-module.js.map +1 -1
  79. package/lib/features/tools/node-creation/node-creation-tool.d.ts +24 -3
  80. package/lib/features/tools/node-creation/node-creation-tool.d.ts.map +1 -1
  81. package/lib/features/tools/node-creation/node-creation-tool.js +41 -11
  82. package/lib/features/tools/node-creation/node-creation-tool.js.map +1 -1
  83. package/lib/index.d.ts +4 -0
  84. package/lib/index.d.ts.map +1 -1
  85. package/lib/index.js +4 -0
  86. package/lib/index.js.map +1 -1
  87. package/lib/tsconfig.tsbuildinfo +1 -1
  88. package/package.json +3 -3
  89. package/src/base/args-feature.ts +12 -2
  90. package/src/base/feedback/css-feedback.ts +4 -1
  91. package/src/default-modules.ts +2 -0
  92. package/src/features/accessibility/keyboard-tool-palette/keyboard-tool-palette-module.ts +13 -3
  93. package/src/features/accessibility/keyboard-tool-palette/keyboard-tool-palette.ts +15 -16
  94. package/src/features/bounds/bounds-module.ts +6 -0
  95. package/src/features/bounds/glsp-hidden-bounds-updater.ts +11 -7
  96. package/src/features/bounds/local-bounds.ts +91 -0
  97. package/src/features/bounds/set-bounds-feedback-command.ts +70 -0
  98. package/src/features/change-bounds/model.ts +2 -3
  99. package/src/features/decoration/decoration-placer.ts +3 -3
  100. package/src/features/element-template/add-template-element.ts +109 -0
  101. package/src/features/element-template/element-template-module.ts +24 -0
  102. package/src/features/element-template/mouse-tracking-element-position-listener.ts +110 -0
  103. package/src/features/element-template/remove-template-element.ts +56 -0
  104. package/src/features/tool-palette/tool-palette-module.ts +3 -1
  105. package/src/features/tool-palette/tool-palette.ts +42 -14
  106. package/src/features/tools/change-bounds/change-bounds-tool.ts +12 -10
  107. package/src/features/tools/change-bounds/view.tsx +1 -1
  108. package/src/features/tools/node-creation/node-creation-module.ts +9 -5
  109. package/src/features/tools/node-creation/node-creation-tool.ts +51 -13
  110. package/src/index.ts +4 -0
@@ -0,0 +1,110 @@
1
+ /********************************************************************************
2
+ * Copyright (c) 2023 EclipseSource and others.
3
+ *
4
+ * This program and the accompanying materials are made available under the
5
+ * terms of the Eclipse Public License v. 2.0 which is available at
6
+ * http://www.eclipse.org/legal/epl-2.0.
7
+ *
8
+ * This Source Code may also be made available under the following Secondary
9
+ * Licenses when the conditions for such availability set forth in the Eclipse
10
+ * Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ * with the GNU Classpath Exception which is available at
12
+ * https://www.gnu.org/software/classpath/license.html.
13
+ *
14
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
+ ********************************************************************************/
16
+
17
+ import { Action, Disposable, GModelElement, ISnapper, MoveAction, Point, isBoundsAware, isMoveable } from '@eclipse-glsp/sprotty';
18
+ import { injectable } from 'inversify';
19
+ import { DragAwareMouseListener } from '../../base/drag-aware-mouse-listener';
20
+ import { CSS_HIDDEN, ModifyCSSFeedbackAction } from '../../base/feedback/css-feedback';
21
+ import { IFeedbackEmitter } from '../../base/feedback/feedback-action-dispatcher';
22
+ import { Tool } from '../../base/tool-manager/tool';
23
+ import { getAbsolutePosition } from '../../utils/viewpoint-util';
24
+ import {
25
+ IMovementRestrictor,
26
+ createMovementRestrictionFeedback,
27
+ removeMovementRestrictionFeedback
28
+ } from '../change-bounds/movement-restrictor';
29
+
30
+ export interface PositioningTool extends Tool {
31
+ readonly snapper?: ISnapper;
32
+ readonly movementRestrictor?: IMovementRestrictor;
33
+
34
+ registerFeedback(feedbackActions: Action[], feedbackEmitter?: IFeedbackEmitter, cleanupActions?: Action[]): Disposable;
35
+ deregisterFeedback(feedbackEmitter?: IFeedbackEmitter, cleanupActions?: Action[]): void;
36
+ }
37
+
38
+ @injectable()
39
+ export class MouseTrackingElementPositionListener extends DragAwareMouseListener {
40
+ element?: GModelElement;
41
+ currentPosition?: Point;
42
+
43
+ constructor(
44
+ protected elementId: string,
45
+ protected tool: PositioningTool,
46
+ protected cursorPosition: 'top-left' | 'middle' = 'top-left'
47
+ ) {
48
+ super();
49
+ }
50
+
51
+ override mouseMove(target: GModelElement, event: MouseEvent): Action[] {
52
+ super.mouseMove(target, event);
53
+ const element = target.root.index.getById(this.elementId);
54
+ this.element = element;
55
+ if (!element) {
56
+ return [];
57
+ }
58
+
59
+ let newPosition = getAbsolutePosition(target, event);
60
+ if (this.cursorPosition === 'middle' && isBoundsAware(element)) {
61
+ newPosition = Point.subtract(newPosition, { x: element.bounds.width / 2, y: element.bounds.height / 2 });
62
+ }
63
+ newPosition = this.snap(newPosition, element);
64
+
65
+ const finished = false;
66
+ if (isMoveable(element)) {
67
+ newPosition = this.validateMove(this.currentPosition ?? newPosition, newPosition, element, finished);
68
+ }
69
+ this.currentPosition = newPosition;
70
+ const moveGhostElement = MoveAction.create(
71
+ [
72
+ {
73
+ elementId: element.id,
74
+ toPosition: newPosition
75
+ }
76
+ ],
77
+ { animate: false, finished }
78
+ );
79
+ this.tool.registerFeedback([moveGhostElement], this);
80
+ return element.cssClasses?.includes(CSS_HIDDEN)
81
+ ? [ModifyCSSFeedbackAction.create({ elements: [element.id], remove: [CSS_HIDDEN] })]
82
+ : [];
83
+ }
84
+
85
+ protected snap(position: Point, element: GModelElement, isSnap = true): Point {
86
+ if (isSnap && this.tool.snapper) {
87
+ return this.tool.snapper.snap(position, element);
88
+ } else {
89
+ return position;
90
+ }
91
+ }
92
+
93
+ protected validateMove(startPosition: Point, toPosition: Point, element: GModelElement, isFinished: boolean): Point {
94
+ let newPosition = toPosition;
95
+ if (this.tool.movementRestrictor) {
96
+ const valid = this.tool.movementRestrictor.validate(element, toPosition);
97
+ let action;
98
+ if (!valid) {
99
+ action = createMovementRestrictionFeedback(element, this.tool.movementRestrictor);
100
+ if (isFinished) {
101
+ newPosition = startPosition;
102
+ }
103
+ } else {
104
+ action = removeMovementRestrictionFeedback(element, this.tool.movementRestrictor);
105
+ }
106
+ this.tool.registerFeedback([action], this, [removeMovementRestrictionFeedback(element, this.tool.movementRestrictor)]);
107
+ }
108
+ return newPosition;
109
+ }
110
+ }
@@ -0,0 +1,56 @@
1
+ /********************************************************************************
2
+ * Copyright (c) 2023 EclipseSource and others.
3
+ *
4
+ * This program and the accompanying materials are made available under the
5
+ * terms of the Eclipse Public License v. 2.0 which is available at
6
+ * http://www.eclipse.org/legal/epl-2.0.
7
+ *
8
+ * This Source Code may also be made available under the following Secondary
9
+ * Licenses when the conditions for such availability set forth in the Eclipse
10
+ * Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ * with the GNU Classpath Exception which is available at
12
+ * https://www.gnu.org/software/classpath/license.html.
13
+ *
14
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
+ ********************************************************************************/
16
+
17
+ import { Action, CommandExecutionContext, CommandReturn, ElementTemplate, GChildElement, TYPES } from '@eclipse-glsp/sprotty';
18
+ import { inject, injectable } from 'inversify';
19
+ import { FeedbackCommand } from '../../base/feedback/feedback-command';
20
+ import { getTemplateElementId } from './add-template-element';
21
+
22
+ export interface RemoveTemplateElementsAction extends Action {
23
+ kind: typeof RemoveTemplateElementsAction.KIND;
24
+ templates: ElementTemplate[];
25
+ }
26
+
27
+ export namespace RemoveTemplateElementsAction {
28
+ export const KIND = 'removeTemplateElements';
29
+
30
+ export function create(options: Omit<RemoveTemplateElementsAction, 'kind'>): RemoveTemplateElementsAction {
31
+ return {
32
+ kind: KIND,
33
+ ...options
34
+ };
35
+ }
36
+ }
37
+
38
+ @injectable()
39
+ export class RemoveTemplateElementsFeedbackCommand extends FeedbackCommand {
40
+ static readonly KIND = RemoveTemplateElementsAction.KIND;
41
+
42
+ constructor(@inject(TYPES.Action) readonly action: RemoveTemplateElementsAction) {
43
+ super();
44
+ }
45
+
46
+ execute(context: CommandExecutionContext): CommandReturn {
47
+ const index = context.root.index;
48
+ for (const template of this.action.templates) {
49
+ const element = index.getById(getTemplateElementId(template));
50
+ if (element && element instanceof GChildElement) {
51
+ element.parent.remove(element);
52
+ }
53
+ }
54
+ return context.root;
55
+ }
56
+ }
@@ -13,7 +13,7 @@
13
13
  *
14
14
  * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
15
  ********************************************************************************/
16
- import { bindAsService, configureActionHandler, FeatureModule, TYPES } from '@eclipse-glsp/sprotty';
16
+ import { bindAsService, configureActionHandler, FeatureModule, SetModelAction, TYPES, UpdateModelAction } from '@eclipse-glsp/sprotty';
17
17
  import '../../../css/tool-palette.css';
18
18
  import { EnableDefaultToolsAction } from '../../base/tool-manager/tool';
19
19
  import { ToolPalette } from './tool-palette';
@@ -22,4 +22,6 @@ export const toolPaletteModule = new FeatureModule((bind, _unbind, isBound, _reb
22
22
  bindAsService(bind, TYPES.IUIExtension, ToolPalette);
23
23
  bind(TYPES.IDiagramStartup).toService(ToolPalette);
24
24
  configureActionHandler({ bind, isBound }, EnableDefaultToolsAction.KIND, ToolPalette);
25
+ configureActionHandler({ bind, isBound }, UpdateModelAction.KIND, ToolPalette);
26
+ configureActionHandler({ bind, isBound }, SetModelAction.KIND, ToolPalette);
25
27
  });
@@ -13,22 +13,25 @@
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, postConstruct } from 'inversify';
17
16
  import {
18
17
  AbstractUIExtension,
19
18
  Action,
19
+ GModelRoot,
20
20
  IActionHandler,
21
21
  ICommand,
22
22
  MarkersReason,
23
23
  PaletteItem,
24
24
  RequestContextActions,
25
25
  RequestMarkersAction,
26
- GModelRoot,
27
26
  SetContextActions,
27
+ SetModelAction,
28
28
  SetUIExtensionVisibilityAction,
29
+ TriggerNodeCreationAction,
30
+ UpdateModelAction,
29
31
  codiconCSSClasses,
30
32
  matchesKeystroke
31
33
  } from '@eclipse-glsp/sprotty';
34
+ import { inject, injectable, postConstruct } from 'inversify';
32
35
  import { GLSPActionDispatcher } from '../../base/action-dispatcher';
33
36
  import { EditorContextService, IEditModeListener } from '../../base/editor-context-service';
34
37
  import { FocusTracker } from '../../base/focus/focus-tracker';
@@ -73,6 +76,7 @@ export class ToolPalette extends AbstractUIExtension implements IActionHandler,
73
76
 
74
77
  protected paletteItems: PaletteItem[];
75
78
  protected paletteItemsCopy: PaletteItem[] = [];
79
+ protected dynamic = false;
76
80
  protected bodyDiv?: HTMLElement;
77
81
  protected lastActiveButton?: HTMLElement;
78
82
  protected defaultToolsButton: HTMLElement;
@@ -333,11 +337,15 @@ export class ToolPalette extends AbstractUIExtension implements IActionHandler,
333
337
  }
334
338
  }
335
339
 
336
- handle(action: EnableDefaultToolsAction): ICommand | Action | void {
340
+ handle(action: Action): ICommand | Action | void {
337
341
  this.changeActiveButton();
338
- if (this.focusTracker.hasFocus) {
339
- // if focus was deliberately taken do not restore focus to the palette
340
- this.focusTracker.diagramElement?.focus();
342
+ if (UpdateModelAction.is(action) || SetModelAction.is(action)) {
343
+ this.reloadPaletteBody();
344
+ } else if (EnableDefaultToolsAction.is(action)) {
345
+ if (this.focusTracker.hasFocus) {
346
+ // if focus was deliberately taken do not restore focus to the palette
347
+ this.focusTracker.diagramElement?.focus();
348
+ }
341
349
  }
342
350
  }
343
351
 
@@ -360,11 +368,6 @@ export class ToolPalette extends AbstractUIExtension implements IActionHandler,
360
368
  }
361
369
  }
362
370
 
363
- protected handleSetContextActions(action: SetContextActions): void {
364
- this.paletteItems = action.actions.map(e => e as PaletteItem);
365
- this.createBody();
366
- }
367
-
368
371
  protected requestFilterUpdate(filter: string): void {
369
372
  // Initialize the copy if it's empty
370
373
  if (this.paletteItemsCopy.length === 0) {
@@ -395,6 +398,17 @@ export class ToolPalette extends AbstractUIExtension implements IActionHandler,
395
398
  }
396
399
 
397
400
  async preRequestModel(): Promise<void> {
401
+ await this.setPaletteItems();
402
+ if (!this.editorContext.isReadonly) {
403
+ this.show(this.editorContext.modelRoot);
404
+ }
405
+ }
406
+
407
+ async postRequestModel(): Promise<void> {
408
+ this.reloadPaletteBody();
409
+ }
410
+
411
+ protected async setPaletteItems(): Promise<void> {
398
412
  const requestAction = RequestContextActions.create({
399
413
  contextId: ToolPalette.ID,
400
414
  editorContext: {
@@ -402,9 +416,23 @@ export class ToolPalette extends AbstractUIExtension implements IActionHandler,
402
416
  }
403
417
  });
404
418
  const response = await this.actionDispatcher.request<SetContextActions>(requestAction);
405
- this.paletteItems = response.actions.map(e => e as PaletteItem);
406
- if (!this.editorContext.isReadonly) {
407
- this.show(this.editorContext.modelRoot);
419
+ this.paletteItems = response.actions.map(action => action as PaletteItem);
420
+ this.dynamic = this.paletteItems.some(item => this.hasDynamicAction(item));
421
+ }
422
+
423
+ protected hasDynamicAction(item: PaletteItem): boolean {
424
+ const dynamic = !!item.actions.find(action => TriggerNodeCreationAction.is(action) && action.ghostElement?.dynamic);
425
+ if (dynamic) {
426
+ return dynamic;
427
+ }
428
+ return item.children?.some(child => this.hasDynamicAction(child)) || false;
429
+ }
430
+
431
+ protected async reloadPaletteBody(): Promise<void> {
432
+ if (this.dynamic) {
433
+ await this.setPaletteItems();
434
+ this.paletteItemsCopy = [];
435
+ this.requestFilterUpdate(this.searchField.value);
408
436
  }
409
437
  }
410
438
  }
@@ -28,15 +28,14 @@ import {
28
28
  ElementAndBounds,
29
29
  ElementAndRoutingPoints,
30
30
  GChildElement,
31
- ISnapper,
32
- MouseListener,
33
- Operation,
34
- Point,
35
31
  GConnectableElement,
36
32
  GModelElement,
37
33
  GModelRoot,
38
34
  GParentElement,
39
- SetBoundsAction,
35
+ ISnapper,
36
+ MouseListener,
37
+ Operation,
38
+ Point,
40
39
  TYPES,
41
40
  findParentByFeature,
42
41
  isSelected
@@ -45,13 +44,14 @@ import { DragAwareMouseListener } from '../../../base/drag-aware-mouse-listener'
45
44
  import { CursorCSS, applyCssClasses, cursorFeedbackAction, deleteCssClasses } from '../../../base/feedback/css-feedback';
46
45
  import { ISelectionListener, SelectionService } from '../../../base/selection-service';
47
46
  import { PointPositionUpdater } from '../../../features/change-bounds/snap';
48
- import { isValidMove, isValidSize } from '../../../utils/layout-utils';
49
47
  import {
50
48
  calcElementAndRoutingPoints,
51
49
  forEachElement,
52
50
  isNonRoutableSelectedMovableBoundsAware,
53
51
  toElementAndBounds
54
52
  } from '../../../utils/gmodel-util';
53
+ import { isValidMove, isValidSize } from '../../../utils/layout-utils';
54
+ import { SetBoundsFeedbackAction } from '../../bounds/set-bounds-feedback-command';
55
55
  import { Resizable, ResizeHandleLocation, SResizeHandle, isBoundsAwareMoveable, isResizable } from '../../change-bounds/model';
56
56
  import {
57
57
  IMovementRestrictor,
@@ -335,7 +335,7 @@ export class ChangeBoundsListener extends DragAwareMouseListener implements ISel
335
335
  if (this.initialBounds && this.activeResizeHandle && resetBounds) {
336
336
  // we only reset the bounds if an active resize operation was cancelled due to the tool being disabled
337
337
  resetFeedback.push(
338
- SetBoundsAction.create([
338
+ SetBoundsFeedbackAction.create([
339
339
  {
340
340
  elementId: this.activeResizeElement.id,
341
341
  newPosition: this.initialBounds,
@@ -424,7 +424,9 @@ export class ChangeBoundsListener extends DragAwareMouseListener implements ISel
424
424
  if (this.tool.movementRestrictor) {
425
425
  actions.push(removeMovementRestrictionFeedback(element, this.tool.movementRestrictor));
426
426
  }
427
- actions.push(SetBoundsAction.create([{ elementId: element.id, newPosition: this.initialBounds, newSize: this.initialBounds }]));
427
+ actions.push(
428
+ SetBoundsFeedbackAction.create([{ elementId: element.id, newPosition: this.initialBounds, newSize: this.initialBounds }])
429
+ );
428
430
  return actions;
429
431
  }
430
432
  return [];
@@ -446,12 +448,12 @@ export class ChangeBoundsListener extends DragAwareMouseListener implements ISel
446
448
  if (this.tool.movementRestrictor) {
447
449
  result.push(removeMovementRestrictionFeedback(element, this.tool.movementRestrictor));
448
450
  }
449
- result.push(SetBoundsAction.create([{ elementId: element.id, newPosition, newSize }]));
451
+ result.push(SetBoundsFeedbackAction.create([{ elementId: element.id, newPosition, newSize }]));
450
452
  } else if (this.isValidSize(element, newSize)) {
451
453
  if (this.tool.movementRestrictor) {
452
454
  result.push(createMovementRestrictionFeedback(element, this.tool.movementRestrictor));
453
455
  }
454
- result.push(SetBoundsAction.create([{ elementId: element.id, newPosition, newSize }]));
456
+ result.push(SetBoundsFeedbackAction.create([{ elementId: element.id, newPosition, newSize }]));
455
457
  }
456
458
 
457
459
  return result;
@@ -13,9 +13,9 @@
13
13
  *
14
14
  * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
15
  ********************************************************************************/
16
+ import { IView, Point, RenderingContext, setAttr, svg } from '@eclipse-glsp/sprotty';
16
17
  import { injectable } from 'inversify';
17
18
  import { VNode } from 'snabbdom';
18
- import { IView, Point, RenderingContext, setAttr, svg } from '@eclipse-glsp/sprotty';
19
19
  import { ResizeHandleLocation, SResizeHandle, isResizable } from '../../change-bounds/model';
20
20
 
21
21
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -14,10 +14,14 @@
14
14
  * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
15
  ********************************************************************************/
16
16
  import { FeatureModule, TYPES, TriggerNodeCreationAction, bindAsService, configureActionHandler } from '@eclipse-glsp/sprotty';
17
+ import { elementTemplateModule } from '../../element-template/element-template-module';
17
18
  import { NodeCreationTool } from './node-creation-tool';
18
19
 
19
- export const nodeCreationToolModule = new FeatureModule((bind, unbind, isBound, rebind) => {
20
- const context = { bind, unbind, isBound, rebind };
21
- bindAsService(context, TYPES.ITool, NodeCreationTool);
22
- configureActionHandler(context, TriggerNodeCreationAction.KIND, NodeCreationTool);
23
- });
20
+ export const nodeCreationToolModule = new FeatureModule(
21
+ (bind, unbind, isBound, rebind) => {
22
+ const context = { bind, unbind, isBound, rebind };
23
+ bindAsService(context, TYPES.ITool, NodeCreationTool);
24
+ configureActionHandler(context, TriggerNodeCreationAction.KIND, NodeCreationTool);
25
+ },
26
+ { requires: elementTemplateModule }
27
+ );
@@ -13,40 +13,61 @@
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, optional } from 'inversify';
17
16
  import {
18
17
  Action,
19
18
  CreateNodeOperation,
20
- ISnapper,
21
19
  GModelElement,
22
20
  GNode,
21
+ ISnapper,
22
+ Point,
23
23
  TYPES,
24
24
  TriggerNodeCreationAction,
25
25
  findParentByFeature,
26
26
  isCtrlOrCmd
27
27
  } from '@eclipse-glsp/sprotty';
28
+ import { inject, injectable, optional } from 'inversify';
29
+ import '../../../../css/ghost-element.css';
28
30
  import { DragAwareMouseListener } from '../../../base/drag-aware-mouse-listener';
29
- import { CursorCSS, cursorFeedbackAction } from '../../../base/feedback/css-feedback';
31
+ import { CSS_GHOST_ELEMENT, CSS_HIDDEN, CursorCSS, cursorFeedbackAction } from '../../../base/feedback/css-feedback';
30
32
  import { EnableDefaultToolsAction } from '../../../base/tool-manager/tool';
31
33
  import { getAbsolutePosition } from '../../../utils/viewpoint-util';
34
+ import { IMovementRestrictor } from '../../change-bounds/movement-restrictor';
35
+ import { AddTemplateElementsAction, getTemplateElementId } from '../../element-template/add-template-element';
36
+ import { MouseTrackingElementPositionListener, PositioningTool } from '../../element-template/mouse-tracking-element-position-listener';
37
+ import { RemoveTemplateElementsAction } from '../../element-template/remove-template-element';
32
38
  import { Containable, isContainable } from '../../hints/model';
33
39
  import { BaseCreationTool } from '../base-tools';
34
40
 
35
41
  @injectable()
36
- export class NodeCreationTool extends BaseCreationTool<TriggerNodeCreationAction> {
42
+ export class NodeCreationTool extends BaseCreationTool<TriggerNodeCreationAction> implements PositioningTool {
37
43
  static ID = 'tool_create_node';
38
44
 
39
45
  protected isTriggerAction = TriggerNodeCreationAction.is;
40
46
 
41
47
  @inject(TYPES.ISnapper) @optional() readonly snapper?: ISnapper;
48
+ @inject(TYPES.IMovementRestrictor) @optional() readonly movementRestrictor?: IMovementRestrictor;
42
49
 
43
50
  get id(): string {
44
51
  return NodeCreationTool.ID;
45
52
  }
46
53
 
47
54
  doEnable(): void {
55
+ let trackingListener: MouseTrackingElementPositionListener | undefined;
56
+ const ghostElement = this.triggerAction.ghostElement;
57
+ if (ghostElement) {
58
+ trackingListener = new MouseTrackingElementPositionListener(getTemplateElementId(ghostElement.template), this, 'middle');
59
+ this.toDisposeOnDisable.push(
60
+ this.registerFeedback(
61
+ [AddTemplateElementsAction.create({ templates: [ghostElement.template], addClasses: [CSS_HIDDEN, CSS_GHOST_ELEMENT] })],
62
+ ghostElement,
63
+ [RemoveTemplateElementsAction.create({ templates: [ghostElement.template] })]
64
+ ),
65
+ this.mouseTool.registerListener(trackingListener)
66
+ );
67
+ }
68
+
48
69
  this.toDisposeOnDisable.push(
49
- this.mouseTool.registerListener(new NodeCreationToolMouseListener(this.triggerAction, this)),
70
+ this.mouseTool.registerListener(new NodeCreationToolMouseListener(this.triggerAction, this, trackingListener)),
50
71
  this.registerFeedback([cursorFeedbackAction(CursorCSS.NODE_CREATION)], this, [cursorFeedbackAction()])
51
72
  );
52
73
  }
@@ -56,7 +77,11 @@ export class NodeCreationTool extends BaseCreationTool<TriggerNodeCreationAction
56
77
  export class NodeCreationToolMouseListener extends DragAwareMouseListener {
57
78
  protected container?: GModelElement & Containable;
58
79
 
59
- constructor(protected triggerAction: TriggerNodeCreationAction, protected tool: NodeCreationTool) {
80
+ constructor(
81
+ protected triggerAction: TriggerNodeCreationAction,
82
+ protected tool: NodeCreationTool,
83
+ protected trackingListener?: MouseTrackingElementPositionListener
84
+ ) {
60
85
  super();
61
86
  }
62
87
 
@@ -76,21 +101,34 @@ export class NodeCreationToolMouseListener extends DragAwareMouseListener {
76
101
 
77
102
  if (this.creationAllowed(this.elementTypeId)) {
78
103
  const containerId = this.container ? this.container.id : undefined;
79
- let location = getAbsolutePosition(target, event);
80
- if (this.tool.snapper) {
81
- // Create a 0-bounds proxy element for snapping
82
- const elementProxy = new GNode();
83
- elementProxy.size = { width: 0, height: 0 };
84
- location = this.tool.snapper.snap(location, elementProxy);
85
- }
104
+ const location = this.getInsertPosition(target, event);
86
105
  result.push(CreateNodeOperation.create(this.elementTypeId, { location, containerId, args: this.triggerAction.args }));
87
106
  if (!isCtrlOrCmd(event)) {
88
107
  result.push(EnableDefaultToolsAction.create());
108
+ } else {
109
+ this.tool.deregisterFeedback(this);
89
110
  }
90
111
  }
91
112
  return result;
92
113
  }
93
114
 
115
+ protected getInsertPosition(target: GModelElement, event: MouseEvent): Point {
116
+ if (this.trackingListener) {
117
+ const trackedPosition = this.trackingListener.currentPosition;
118
+ if (trackedPosition) {
119
+ return trackedPosition;
120
+ }
121
+ }
122
+ let location = getAbsolutePosition(target, event);
123
+ if (this.tool.snapper) {
124
+ // Create a 0-bounds proxy element for snapping
125
+ const elementProxy = new GNode();
126
+ elementProxy.size = { width: 0, height: 0 };
127
+ location = this.tool.snapper.snap(location, elementProxy);
128
+ }
129
+ return location;
130
+ }
131
+
94
132
  override mouseOver(target: GModelElement, event: MouseEvent): Action[] {
95
133
  const currentContainer = findParentByFeature(target, isContainable);
96
134
  if (!this.container || currentContainer !== this.container) {
package/src/index.ts CHANGED
@@ -70,6 +70,9 @@ export * from './features/context-menu/server-context-menu-provider';
70
70
  export * from './features/copy-paste/copy-paste-context-menu';
71
71
  export * from './features/copy-paste/copy-paste-handler';
72
72
  export * from './features/decoration/decoration-placer';
73
+ export * from './features/element-template/add-template-element';
74
+ export * from './features/element-template/mouse-tracking-element-position-listener';
75
+ export * from './features/element-template/remove-template-element';
73
76
  export * from './features/export/glsp-svg-exporter';
74
77
  export * from './features/hints/model';
75
78
  export * from './features/hints/type-hint-provider';
@@ -136,6 +139,7 @@ export * from './features/command-palette/command-palette-module';
136
139
  export * from './features/context-menu/context-menu-module';
137
140
  export * from './features/copy-paste/copy-paste-modules';
138
141
  export * from './features/decoration/decoration-module';
142
+ export * from './features/element-template/element-template-module';
139
143
  export * from './features/export/export-modules';
140
144
  export * from './features/hints/type-hints-module';
141
145
  export * from './features/hover/hover-module';