@eclipse-glsp-examples/workflow-glsp 0.9.0-next.9ac2fc1e

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.
@@ -0,0 +1,152 @@
1
+ /********************************************************************************
2
+ * Copyright (c) 2019-2021 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
+ import '../css/diagram.css';
17
+ import 'balloon-css/balloon.min.css';
18
+ import 'sprotty/css/edit-label.css';
19
+
20
+ import {
21
+ boundsModule,
22
+ buttonModule,
23
+ configureDefaultModelElements,
24
+ configureModelElement,
25
+ ConsoleLogger,
26
+ defaultGLSPModule,
27
+ defaultModule,
28
+ DefaultTypes,
29
+ DeleteElementContextMenuItemProvider,
30
+ DiamondNodeView,
31
+ edgeIntersectionModule,
32
+ edgeLayoutModule,
33
+ editLabelFeature,
34
+ expandModule,
35
+ exportModule,
36
+ fadeModule,
37
+ GLSP_TYPES,
38
+ glspCommandPaletteModule,
39
+ glspContextMenuModule,
40
+ glspDecorationModule,
41
+ glspEditLabelModule,
42
+ glspHoverModule,
43
+ glspMouseToolModule,
44
+ glspSelectModule,
45
+ glspServerCopyPasteModule,
46
+ glspViewportModule,
47
+ GridSnapper,
48
+ labelEditUiModule,
49
+ layoutCommandsModule,
50
+ LogLevel,
51
+ markerNavigatorModule,
52
+ modelHintsModule,
53
+ modelSourceModule,
54
+ modelSourceWatcherModule,
55
+ navigationModule,
56
+ NoOverlapMovmentRestrictor,
57
+ openModule,
58
+ overrideViewerOptions,
59
+ paletteModule,
60
+ RectangularNodeView,
61
+ RevealNamedElementActionProvider,
62
+ RoundedCornerNodeView,
63
+ routingModule,
64
+ SCompartment,
65
+ SCompartmentView,
66
+ SEdge,
67
+ SLabel,
68
+ SLabelView,
69
+ toolFeedbackModule,
70
+ toolsModule,
71
+ TYPES,
72
+ validationModule,
73
+ zorderModule
74
+ } from '@eclipse-glsp/client';
75
+ import { Container, ContainerModule } from 'inversify';
76
+
77
+ import { directTaskEditor } from './direct-task-editing/di.config';
78
+ import { ActivityNode, Icon, TaskNode, WeightedEdge } from './model';
79
+ import { IconView, WorkflowEdgeView } from './workflow-views';
80
+
81
+ const workflowDiagramModule = new ContainerModule((bind, unbind, isBound, rebind) => {
82
+ rebind(TYPES.ILogger).to(ConsoleLogger).inSingletonScope();
83
+ rebind(TYPES.LogLevel).toConstantValue(LogLevel.warn);
84
+ bind(GLSP_TYPES.IMovementRestrictor).to(NoOverlapMovmentRestrictor).inSingletonScope();
85
+ bind(TYPES.ISnapper).to(GridSnapper);
86
+ bind(TYPES.ICommandPaletteActionProvider).to(RevealNamedElementActionProvider);
87
+ bind(TYPES.IContextMenuItemProvider).to(DeleteElementContextMenuItemProvider);
88
+ const context = { bind, unbind, isBound, rebind };
89
+
90
+ configureDefaultModelElements(context);
91
+ configureModelElement(context, 'task:automated', TaskNode, RoundedCornerNodeView);
92
+ configureModelElement(context, 'task:manual', TaskNode, RoundedCornerNodeView);
93
+ configureModelElement(context, 'label:heading', SLabel, SLabelView, { enable: [editLabelFeature] });
94
+ configureModelElement(context, 'comp:comp', SCompartment, SCompartmentView);
95
+ configureModelElement(context, 'comp:header', SCompartment, SCompartmentView);
96
+ configureModelElement(context, 'label:icon', SLabel, SLabelView);
97
+ configureModelElement(context, DefaultTypes.EDGE, SEdge, WorkflowEdgeView);
98
+ configureModelElement(context, 'edge:weighted', WeightedEdge, WorkflowEdgeView);
99
+ configureModelElement(context, 'icon', Icon, IconView);
100
+ configureModelElement(context, 'activityNode:merge', ActivityNode, DiamondNodeView);
101
+ configureModelElement(context, 'activityNode:decision', ActivityNode, DiamondNodeView);
102
+ configureModelElement(context, 'activityNode:fork', ActivityNode, RectangularNodeView);
103
+ configureModelElement(context, 'activityNode:join', ActivityNode, RectangularNodeView);
104
+ });
105
+
106
+ export default function createContainer(widgetId: string): Container {
107
+ const container = new Container();
108
+
109
+ container.load(
110
+ defaultModule,
111
+ defaultGLSPModule,
112
+ glspMouseToolModule,
113
+ validationModule,
114
+ glspSelectModule,
115
+ boundsModule,
116
+ glspViewportModule,
117
+ toolsModule,
118
+ glspHoverModule,
119
+ fadeModule,
120
+ exportModule,
121
+ expandModule,
122
+ openModule,
123
+ buttonModule,
124
+ modelSourceModule,
125
+ labelEditUiModule,
126
+ glspEditLabelModule,
127
+ workflowDiagramModule,
128
+ toolFeedbackModule,
129
+ modelHintsModule,
130
+ glspContextMenuModule,
131
+ glspServerCopyPasteModule,
132
+ modelSourceWatcherModule,
133
+ glspCommandPaletteModule,
134
+ paletteModule,
135
+ routingModule,
136
+ glspDecorationModule,
137
+ edgeLayoutModule,
138
+ zorderModule,
139
+ edgeIntersectionModule,
140
+ layoutCommandsModule,
141
+ directTaskEditor,
142
+ navigationModule,
143
+ markerNavigatorModule
144
+ );
145
+
146
+ overrideViewerOptions(container, {
147
+ baseDiv: widgetId,
148
+ hiddenDiv: widgetId + '_hidden'
149
+ });
150
+
151
+ return container;
152
+ }
@@ -0,0 +1,24 @@
1
+ /********************************************************************************
2
+ * Copyright (c) 2020 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
+ import { ContainerModule } from 'inversify';
17
+ import { TYPES } from 'sprotty';
18
+
19
+ import { TaskEditor } from './direct-task-editor';
20
+
21
+ export const directTaskEditor = new ContainerModule((bind, _unbind, isBound) => {
22
+ bind(TaskEditor).toSelf().inSingletonScope();
23
+ bind(TYPES.IUIExtension).toService(TaskEditor);
24
+ });
@@ -0,0 +1,162 @@
1
+ /********************************************************************************
2
+ * Copyright (c) 2020-2021 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
+ import {
17
+ AbstractUIExtension,
18
+ Action,
19
+ AutoCompleteWidget,
20
+ EditorContextService,
21
+ getAbsoluteClientBounds,
22
+ GLSPActionDispatcher,
23
+ ILogger,
24
+ isSetContextActionsAction,
25
+ isSetEditValidationResultAction,
26
+ LabeledAction,
27
+ RequestContextActions,
28
+ RequestEditValidationAction,
29
+ SModelElement,
30
+ SModelRoot,
31
+ toActionArray,
32
+ ValidationDecorator,
33
+ ValidationStatus,
34
+ ViewerOptions
35
+ } from '@eclipse-glsp/client';
36
+ import { inject, injectable } from 'inversify';
37
+ import { TYPES } from 'sprotty/lib';
38
+ import { DOMHelper } from 'sprotty/lib/base/views/dom-helper';
39
+
40
+ import { isTaskNode, TaskNode } from '../model';
41
+
42
+ export class ApplyTaskEditOperation implements Action {
43
+ static readonly KIND = 'applyTaskEdit';
44
+ readonly kind = ApplyTaskEditOperation.KIND;
45
+ constructor(readonly taskId: string, readonly expression: string) {}
46
+ }
47
+
48
+ @injectable()
49
+ export class TaskEditor extends AbstractUIExtension {
50
+ static readonly ID = 'task-editor';
51
+ readonly autoSuggestionSettings = {
52
+ noSuggestionsMessage: 'No suggestions available',
53
+ suggestionsClass: 'command-palette-suggestions',
54
+ debounceWaitMs: 50,
55
+ showOnFocus: true
56
+ };
57
+
58
+ @inject(TYPES.IActionDispatcher)
59
+ protected actionDispatcher: GLSPActionDispatcher;
60
+
61
+ @inject(EditorContextService)
62
+ protected editorContextService: EditorContextService;
63
+
64
+ @inject(TYPES.ViewerOptions)
65
+ protected viewerOptions: ViewerOptions;
66
+
67
+ @inject(TYPES.DOMHelper)
68
+ protected domHelper: DOMHelper;
69
+
70
+ @inject(TYPES.ILogger)
71
+ protected logger: ILogger;
72
+
73
+ protected task: TaskNode;
74
+ protected autoSuggestion: AutoCompleteWidget;
75
+
76
+ id(): string {
77
+ return TaskEditor.ID;
78
+ }
79
+ containerClass(): string {
80
+ return 'command-palette';
81
+ }
82
+
83
+ protected initializeContents(containerElement: HTMLElement): void {
84
+ this.autoSuggestion = new AutoCompleteWidget(
85
+ this.autoSuggestionSettings,
86
+ { provideSuggestions: input => this.retrieveSuggestions(input) },
87
+ { executeFromSuggestion: input => this.executeFromSuggestion(input) },
88
+ () => this.hide(),
89
+ this.logger
90
+ );
91
+ this.autoSuggestion.configureValidation(
92
+ { validate: input => this.validateInput(input) },
93
+ new ValidationDecorator(containerElement)
94
+ );
95
+ this.autoSuggestion.configureTextSubmitHandler({
96
+ executeFromTextOnlyInput: (input: string) => this.executeFromTextOnlyInput(input)
97
+ });
98
+ this.autoSuggestion.initialize(containerElement);
99
+ }
100
+
101
+ show(root: Readonly<SModelRoot>, ...contextElementIds: string[]): void {
102
+ super.show(root, ...contextElementIds);
103
+ this.autoSuggestion.open(root);
104
+ }
105
+
106
+ protected onBeforeShow(containerElement: HTMLElement, root: Readonly<SModelRoot>, ...contextElementIds: string[]): void {
107
+ this.task = getTask(contextElementIds, root)[0];
108
+ this.autoSuggestion.inputField.value = '';
109
+ this.setPosition(containerElement);
110
+ }
111
+
112
+ protected setPosition(containerElement: HTMLElement): void {
113
+ let x = 0;
114
+ let y = 0;
115
+
116
+ if (this.task) {
117
+ const bounds = getAbsoluteClientBounds(this.task, this.domHelper, this.viewerOptions);
118
+ x = bounds.x + 5;
119
+ y = bounds.y + 5;
120
+ }
121
+
122
+ containerElement.style.left = `${x}px`;
123
+ containerElement.style.top = `${y}px`;
124
+ containerElement.style.width = '200px';
125
+ }
126
+
127
+ protected async retrieveSuggestions(input: string): Promise<LabeledAction[]> {
128
+ const response = await this.actionDispatcher.request(
129
+ new RequestContextActions(TaskEditor.ID, this.editorContextService.get({ ['text']: input }))
130
+ );
131
+ if (isSetContextActionsAction(response)) {
132
+ return response.actions;
133
+ }
134
+ return Promise.reject();
135
+ }
136
+
137
+ protected async validateInput(input: string): Promise<ValidationStatus> {
138
+ const response = await this.actionDispatcher.request(new RequestEditValidationAction(TaskEditor.ID, this.task.id, input));
139
+ if (isSetEditValidationResultAction(response)) {
140
+ return response.status;
141
+ }
142
+ return Promise.reject();
143
+ }
144
+
145
+ protected executeFromSuggestion(input: LabeledAction | Action[] | Action): void {
146
+ this.actionDispatcher.dispatchAll(toActionArray(input));
147
+ }
148
+
149
+ protected executeFromTextOnlyInput(input: string): void {
150
+ const action = new ApplyTaskEditOperation(this.task.id, input);
151
+ this.actionDispatcher.dispatch(action);
152
+ }
153
+
154
+ hide(): void {
155
+ this.autoSuggestion.dispose();
156
+ super.hide();
157
+ }
158
+ }
159
+
160
+ function getTask(ids: string[], element: SModelElement): TaskNode[] {
161
+ return ids.map(id => element.index.getById(id)).filter(isTaskNode);
162
+ }
package/src/index.ts ADDED
@@ -0,0 +1,21 @@
1
+ /********************************************************************************
2
+ * Copyright (c) 2019-2021 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
+ import createWorkflowDiagramContainer from './di.config';
17
+
18
+ export * from './model';
19
+ export * from './workflow-views';
20
+ export * from './direct-task-editing/direct-task-editor';
21
+ export { createWorkflowDiagramContainer };
package/src/model.ts ADDED
@@ -0,0 +1,111 @@
1
+ /********************************************************************************
2
+ * Copyright (c) 2020-2021 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
+ import {
17
+ boundsFeature,
18
+ connectableFeature,
19
+ deletableFeature,
20
+ DiamondNode,
21
+ EditableLabel,
22
+ fadeFeature,
23
+ hoverFeedbackFeature,
24
+ isEditableLabel,
25
+ layoutableChildFeature,
26
+ LayoutContainer,
27
+ layoutContainerFeature,
28
+ moveFeature,
29
+ Nameable,
30
+ nameFeature,
31
+ popupFeature,
32
+ RectangularNode,
33
+ SChildElement,
34
+ SEdge,
35
+ selectFeature,
36
+ SModelElement,
37
+ SShapeElement,
38
+ WithEditableLabel,
39
+ withEditLabelFeature
40
+ } from '@eclipse-glsp/client';
41
+
42
+ export class TaskNode extends RectangularNode implements Nameable, WithEditableLabel {
43
+ static readonly DEFAULT_FEATURES = [
44
+ connectableFeature,
45
+ deletableFeature,
46
+ selectFeature,
47
+ boundsFeature,
48
+ moveFeature,
49
+ layoutContainerFeature,
50
+ fadeFeature,
51
+ hoverFeedbackFeature,
52
+ popupFeature,
53
+ nameFeature,
54
+ withEditLabelFeature
55
+ ];
56
+ name = '';
57
+ duration?: number;
58
+ taskType?: string;
59
+ reference?: string;
60
+
61
+ get editableLabel(): (SChildElement & EditableLabel) | undefined {
62
+ const headerComp = this.children.find(element => element.type === 'comp:header');
63
+ if (headerComp) {
64
+ const label = headerComp.children.find(element => element.type === 'label:heading');
65
+ if (label && isEditableLabel(label)) {
66
+ return label;
67
+ }
68
+ }
69
+ return undefined;
70
+ }
71
+ }
72
+
73
+ export function isTaskNode(element: SModelElement): element is TaskNode {
74
+ return element instanceof TaskNode || false;
75
+ }
76
+
77
+ export class WeightedEdge extends SEdge {
78
+ probability?: string;
79
+ }
80
+
81
+ export class ActivityNode extends DiamondNode {
82
+ nodeType: string = ActivityNode.Type.UNDEFINED;
83
+ size = {
84
+ width: 32,
85
+ height: 32
86
+ };
87
+ strokeWidth = 1;
88
+ }
89
+
90
+ export namespace ActivityNode {
91
+ export namespace Type {
92
+ export const INITIAL = 'initalNode';
93
+ export const FINAL = 'finalNode';
94
+ export const DECISION = 'decisionNode';
95
+ export const MERGE = 'mergeNode';
96
+ export const JOIN = 'joinNode';
97
+ export const FORK = 'forkNode';
98
+ export const UNDEFINED = 'undefined';
99
+ }
100
+ }
101
+
102
+ export class Icon extends SShapeElement implements LayoutContainer {
103
+ static readonly DEFAULT_FEATURES = [boundsFeature, layoutContainerFeature, layoutableChildFeature, fadeFeature];
104
+
105
+ layout: string;
106
+ layoutOptions?: { [key: string]: string | number | boolean };
107
+ size = {
108
+ width: 32,
109
+ height: 32
110
+ };
111
+ }
@@ -0,0 +1,70 @@
1
+ /********************************************************************************
2
+ * Copyright (c) 2019-2021 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
+ import {
17
+ angleOfPoint,
18
+ IView,
19
+ Point,
20
+ PolylineEdgeViewWithGapsOnIntersections,
21
+ RenderingContext,
22
+ SEdge,
23
+ toDegrees
24
+ } from '@eclipse-glsp/client';
25
+ import { injectable } from 'inversify';
26
+ import { VNode } from 'snabbdom';
27
+ import { svg } from 'sprotty';
28
+
29
+ import { Icon } from './model';
30
+
31
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
32
+ const JSX = { createElement: svg };
33
+
34
+ @injectable()
35
+ export class WorkflowEdgeView extends PolylineEdgeViewWithGapsOnIntersections {
36
+ protected renderAdditionals(edge: SEdge, segments: Point[], context: RenderingContext): VNode[] {
37
+ const additionals = super.renderAdditionals(edge, segments, context);
38
+ const p1 = segments[segments.length - 2];
39
+ const p2 = segments[segments.length - 1];
40
+ const arrow = (
41
+ <path
42
+ class-sprotty-edge={true}
43
+ class-arrow={true}
44
+ d='M 1.5,0 L 10,-4 L 10,4 Z'
45
+ transform={`rotate(${toDegrees(angleOfPoint({ x: p1.x - p2.x, y: p1.y - p2.y }))} ${p2.x} ${p2.y}) translate(${p2.x} ${
46
+ p2.y
47
+ })`}
48
+ />
49
+ );
50
+ additionals.push(arrow);
51
+ return additionals;
52
+ }
53
+ }
54
+
55
+ @injectable()
56
+ export class IconView implements IView {
57
+ render(element: Icon, context: RenderingContext): VNode {
58
+ const radius = this.getRadius();
59
+ return (
60
+ <g>
61
+ <circle class-sprotty-icon={true} r={radius} cx={radius} cy={radius}></circle>
62
+ {context.renderChildren(element)}
63
+ </g>
64
+ );
65
+ }
66
+
67
+ getRadius(): number {
68
+ return 16;
69
+ }
70
+ }