From 2cdca536570640a07c1b1fb710ff8827eded4269 Mon Sep 17 00:00:00 2001 From: Guillaume Fontorbe Date: Wed, 24 Jan 2024 14:33:34 +0100 Subject: [PATCH] Adds flowchart example (#416) * Adds flowchart example Signed-off-by: Guillaume Fontorbe * Changed svg container height Signed-off-by: Guillaume Fontorbe --------- Signed-off-by: Guillaume Fontorbe --- examples/browser-app.ts | 5 +- examples/flowchart/css/diagram.css | 102 ++++ examples/flowchart/css/page.css | 50 ++ examples/flowchart/flowchart.html | 57 +++ examples/flowchart/src/data.ts | 670 +++++++++++++++++++++++++ examples/flowchart/src/di.config.ts | 76 +++ examples/flowchart/src/model-source.ts | 27 + examples/flowchart/src/standalone.ts | 38 ++ examples/flowchart/src/views.tsx | 118 +++++ examples/index.html | 2 + 10 files changed, 1144 insertions(+), 1 deletion(-) create mode 100644 examples/flowchart/css/diagram.css create mode 100644 examples/flowchart/css/page.css create mode 100644 examples/flowchart/flowchart.html create mode 100644 examples/flowchart/src/data.ts create mode 100644 examples/flowchart/src/di.config.ts create mode 100644 examples/flowchart/src/model-source.ts create mode 100644 examples/flowchart/src/standalone.ts create mode 100644 examples/flowchart/src/views.tsx diff --git a/examples/browser-app.ts b/examples/browser-app.ts index 3b1043f7..ee6a51e3 100644 --- a/examples/browser-app.ts +++ b/examples/browser-app.ts @@ -22,8 +22,9 @@ import runRandomGraph from "./random-graph/src/standalone"; import runRandomGraphDistributed from "./random-graph-distributed/src/standalone"; import runSvgPreRendered from "./svg/src/standalone"; import runMulticore from "./multicore/src/multicore"; +import runFlowchart from "./flowchart/src/standalone"; -const appDiv = document.getElementById('sprotty-app') +const appDiv = document.getElementById('sprotty-app'); if (appDiv) { const appMode = appDiv.getAttribute('data-app'); if (appMode === 'circlegraph') @@ -38,6 +39,8 @@ if (appDiv) { runSvgPreRendered(); else if (appMode === 'multicore') runMulticore(); + else if (appMode === 'flowchart') + runFlowchart(); else throw new Error('Dunno what to do :-('); } diff --git a/examples/flowchart/css/diagram.css b/examples/flowchart/css/diagram.css new file mode 100644 index 00000000..7751e8d7 --- /dev/null +++ b/examples/flowchart/css/diagram.css @@ -0,0 +1,102 @@ +/******************************************************************************** + * Copyright (c) 2024 TypeFox and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ + +:root { + --black: #000; + --white: #fff; + --stroke-normal: 2; + --stroke-medium: 3; + --stroke-bold: 5; + --red-500: #f44336; + --yellow-500: #ffeb3b; + --amber-500: #ffc107; +} + +/* graph */ +.sprotty-graph { + font-size: 15pt; +} + +/* nodes */ +.sprotty-node { + stroke: var(--black); + stroke-width: var(--stroke-normal); +} + +.sprotty-node.terminal { + fill: var(--red-500); +} + +.sprotty-node.process { + fill: var(--yellow-500); +} + +.sprotty-node.decision { + fill: var(--amber-500); +} + +.sprotty-node.selected { + stroke-width: var(--stroke-bold); +} + +.sprotty-node.mouseover:not(.selected) { + stroke-width: var(--stroke-medium); +} + +/* labels */ +.sprotty-label { + stroke-width: 0; + fill: var(--black); + font-size: 100%; + font-weight: inherit; + text-anchor: middle; +} + +.edge-label-background { + fill: var(--white); + stroke: none; +} + +/* edges */ +.sprotty-edge { + fill: none; + stroke: var(--black); + stroke-width: var(--stroke-normal); +} + +.sprotty-edge.mouseover:not(.selected) { + stroke-width: var(--stroke-medium); +} + +.sprotty-edge.selected { + stroke: #844; + stroke-width: var(--stroke-bold); +} + +.sprotty-edge>.sprotty-routing-handle { + fill: #884; + stroke: none; + z-index: 1000; +} + +.sprotty-edge>.sprotty-routing-handle[data-kind='line'] { + opacity: 0.35; +} + +.arrowhead { + fill: var(--black); + stroke: var(--black); +} diff --git a/examples/flowchart/css/page.css b/examples/flowchart/css/page.css new file mode 100644 index 00000000..88211497 --- /dev/null +++ b/examples/flowchart/css/page.css @@ -0,0 +1,50 @@ +/******************************************************************************** + * Copyright (c) 2024 TypeFox and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ + + +.footer { + margin-top: 10px; + text-align: right; + font-size: 10px; + color: #888; + + display: flex; + justify-content: space-between; +} + +.shortcuts { + text-align: left; +} + +.copyright { + text-align: right; +} + +.help { + margin-top: 24px; + text-align: right; + font-size: 16px; + color: #888; +} + +svg { + margin-top: 15px; + width: 100%; + height: 600px; + border-style: solid; + border-width: 1px; + border-color: #bbb; +} diff --git a/examples/flowchart/flowchart.html b/examples/flowchart/flowchart.html new file mode 100644 index 00000000..2fdf76e0 --- /dev/null +++ b/examples/flowchart/flowchart.html @@ -0,0 +1,57 @@ + + + + + + Sprotty Flowchart Example + + + + + + + + + +
+
+
+

Sprotty Flowchart Example

+
+
+ Help +
+
+
+
+
+
+ +
+
+ + + \ No newline at end of file diff --git a/examples/flowchart/src/data.ts b/examples/flowchart/src/data.ts new file mode 100644 index 00000000..14f5eac5 --- /dev/null +++ b/examples/flowchart/src/data.ts @@ -0,0 +1,670 @@ +/******************************************************************************** + * Copyright (c) 2024 TypeFox and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ + +import { SEdge, SGraph, SLabel, SModelRoot, SNode } from "sprotty-protocol"; + +export function initializeModel(): SModelRoot { + const nodes: SNode[] = []; + const edges: SEdge[] = []; + + const node0: SNode = { + id: '0', + type: 'node:terminal', + position: { x: 150, y: 10 }, + children: [ + { + id: '0_label', + text: 'Start', + type: 'label', + }], + layout: 'stack', + layoutOptions: { + hAlign: 'center', + vAlign: 'center', + paddingTop: 10, + paddingBottom: 10, + paddingLeft: 20, + paddingRight: 20, + } + }; + nodes.push(node0); + + const node1: SNode = { + id: '1', + type: 'node:process', + position: { x: 109.15, y: 153.9 }, + children: [{ + id: '1_label', + text: 'Patient arrives', + type: 'label' + }], + layout: 'stack', + layoutOptions: { + hAlign: 'center', + vAlign: 'center', + paddingTop: 10, + paddingBottom: 10, + paddingLeft: 20, + paddingRight: 20, + } + }; + nodes.push(node1); + + const node2: SNode = { + id: '2', + type: 'node:decision', + position: { x: 71.4, y: 297.8 }, + children: [{ + id: '2_label', + text: 'Registered patient', + type: 'label' + }], + layout: 'stack', + layoutOptions: { + hAlign: 'center', + vAlign: 'center', + paddingTop: 10, + paddingBottom: 10, + paddingLeft: 40, + paddingRight: 40, + minHeight: 100 + } + + }; + nodes.push(node2); + + const node3: SNode = { + id: '3', + type: 'node:process', + position: { x: 102.35, y: 517.8 }, + children: [{ + id: '3_label', + text: 'Register patient', + type: 'label' + }], + layout: 'stack', + layoutOptions: { + hAlign: 'center', + vAlign: 'center', + paddingTop: 10, + paddingBottom: 10, + paddingLeft: 20, + paddingRight: 20, + } + }; + nodes.push(node3); + + const node4: SNode = { + id: '4', + type: 'node:decision', + position: { x: 411.1, y: 297.8 }, + children: [{ + id: '4_label', + text: 'Available nurse', + type: 'label' + }], + layout: 'stack', + layoutOptions: { + hAlign: 'center', + vAlign: 'center', + paddingTop: 10, + paddingBottom: 10, + paddingLeft: 40, + paddingRight: 40, + minHeight: 100 + } + + }; + nodes.push(node4); + + const node5: SNode = { + id: '5', + type: 'node:process', + position: { x: 395.7, y: 517.8}, + children: [{ + id: '5_label', + text: 'Wait for available nurse', + type: 'label' + }], + layout: 'stack', + layoutOptions: { + hAlign: 'center', + vAlign: 'center', + paddingTop: 10, + paddingBottom: 10, + paddingLeft: 20, + paddingRight: 20, + } + + }; + nodes.push(node5); + + const node6: SNode = { + id: '6', + type: 'node:process', + position: { x: 722.3, y: 335.85 }, + children: [{ + id: '6_label', + text: 'Record health condition', + type: 'label' + }], + layout: 'stack', + layoutOptions: { + hAlign: 'center', + vAlign: 'center', + paddingTop: 10, + paddingBottom: 10, + paddingLeft: 20, + paddingRight: 20, + } + + }; + nodes.push(node6); + + const node7: SNode = { + id: '7', + type: 'node:decision', + position: { x: 736, y: 479.75 }, + children: [{ + id: '7_label', + text: 'Available doctor', + type: 'label' + }], + layout: 'stack', + layoutOptions: { + hAlign: 'center', + vAlign: 'center', + paddingTop: 10, + paddingBottom: 10, + paddingLeft: 40, + paddingRight: 40, + minHeight: 100 + } + + }; + nodes.push(node7); + + const node8: SNode = { + id: '8', + type: 'node:process', + position: { x: 720.5, y: 699.75}, + children: [{ + id: '8_label', + text: 'Wait for available doctor', + type: 'label' + }], + layout: 'stack', + layoutOptions: { + hAlign: 'center', + vAlign: 'center', + paddingTop: 10, + paddingBottom: 10, + paddingLeft: 20, + paddingRight: 20, + } + + }; + nodes.push(node8); + + const node9: SNode = { + id: '9', + type: 'node:process', + position: { x: 1055.7, y: 517.8 }, + children: [{ + id: '9_label', + text: 'Assign patient to doctor', + type: 'label' + }], + layout: 'stack', + layoutOptions: { + hAlign: 'center', + vAlign: 'center', + paddingTop: 10, + paddingBottom: 10, + paddingLeft: 20, + paddingRight: 20, + } + + }; + nodes.push(node9); + + const node10: SNode = { + id: '10', + type: 'node:decision', + position: { x: 1074.9, y: 661.7 }, + children: [{ + id: '10_label', + text: 'Need follow up', + type: 'label' + }], + layout: 'stack', + layoutOptions: { + hAlign: 'center', + vAlign: 'center', + paddingTop: 10, + paddingBottom: 10, + paddingLeft: 40, + paddingRight: 40, + minHeight: 100 + } + + }; + nodes.push(node10); + + const node11: SNode = { + id: '11', + type: 'node:decision', + position: { x: 1065.75, y: 881.7 }, + children: [{ + id: '11_label', + text: 'Need medication', + type: 'label' + }], + layout: 'stack', + layoutOptions: { + hAlign: 'center', + vAlign: 'center', + paddingTop: 10, + paddingBottom: 10, + paddingLeft: 40, + paddingRight: 40, + minHeight: 100 + } + + }; + nodes.push(node11); + + const node12: SNode = { + id: '12', + type: 'node:process', + position: { x: 1099.7, y: 1101.7 }, + children: [{ + id: '12_label', + text: 'Patient leaves', + type: 'label' + }], + layout: 'stack', + layoutOptions: { + hAlign: 'center', + vAlign: 'center', + paddingTop: 10, + paddingBottom: 10, + paddingLeft: 20, + paddingRight: 20, + } + + }; + nodes.push(node12); + + const node13: SNode = { + id: '13', + type: 'node:terminal', + position: { x: 1143.25, y: 1245.6 }, + children: [{ + id: '13_label', + text: 'End', + type: 'label' + }], + layout: 'stack', + layoutOptions: { + hAlign: 'center', + vAlign: 'center', + paddingTop: 10, + paddingBottom: 10, + paddingLeft: 20, + paddingRight: 20, + } + + }; + nodes.push(node13); + + const node14: SNode = { + id: '14', + type: 'node:process', + position: { x: 1385.4, y: 699.75 }, + children: [{ + id: '14_label', + text: 'Arrange appointment', + type: 'label' + }], + layout: 'stack', + layoutOptions: { + hAlign: 'center', + vAlign: 'center', + paddingTop: 10, + paddingBottom: 10, + paddingLeft: 20, + paddingRight: 20, + } + + }; + nodes.push(node14); + + const node15: SNode = { + id: '15', + type: 'node:process', + position: { x: 1386.1, y: 919.75 }, + children: [{ + id: '15_label', + text: 'Prescribe medication', + type: 'label' + }], + layout: 'stack', + layoutOptions: { + hAlign: 'center', + vAlign: 'center', + paddingTop: 10, + paddingBottom: 10, + paddingLeft: 20, + paddingRight: 20, + } + + }; + nodes.push(node15); + + const edge0: SEdge = { + id: '0-1', + type: 'edge', + sourceId: '0', + targetId: '1', + routerKind: 'manhattan' + }; + edges.push(edge0); + + const edge1: SEdge = { + id: '1-2', + type: 'edge', + sourceId: '1', + targetId: '2', + routerKind: 'manhattan', + }; + edges.push(edge1); + + const edge2: SEdge = { + id: '2-3', + type: 'edge', + sourceId: '2', + targetId: '3', + routerKind: 'manhattan', + children: [{ + id: '2-3_label', + text: 'No', + type: 'label:edge', + edgePlacement: { + position: 0.5, + side: 'on', + rotate: false + } + }] + }; + edges.push(edge2); + + const edge3: SEdge = { + id: '3-2', + type: 'edge', + sourceId: '3', + targetId: '2', + routerKind: 'manhattan', + routingPoints: [{ x: 52.35, y: 539.75 }, { x: 52.35, y: 357.8 }], + }; + edges.push(edge3); + + const edge4: SEdge = { + id: '2-4', + type: 'edge', + sourceId: '2', + targetId: '4', + routerKind: 'manhattan', + children: [{ + id: '2-4_label', + text: 'Yes', + type: 'label:edge', + edgePlacement: { + position: 0.5, + side: 'on', + rotate: false + } + }] + }; + edges.push(edge4); + + const edge5: SEdge = { + id: '4-5', + type: 'edge', + sourceId: '4', + targetId: '5', + routerKind: 'manhattan', + children: [{ + id: '4-5_label', + text: 'No', + type: 'label:edge', + edgePlacement: { + position: 0.5, + side: 'on', + rotate: false + } + }] + }; + edges.push(edge5); + + const edge6: SEdge = { + id: '5-4', + type: 'edge', + sourceId: '5', + targetId: '4', + routerKind: 'manhattan', + routingPoints: [{ x: 362, y: 539.75 }, { x: 362, y: 357.8 }], + }; + edges.push(edge6); + + const edge7: SEdge = { + id: '4-6', + type: 'edge', + sourceId: '4', + targetId: '6', + routerKind: 'manhattan', + children: [{ + id: '4-6_label', + text: 'Yes', + type: 'label:edge', + edgePlacement: { + position: 0.5, + side: 'on', + rotate: false + } + }] + }; + edges.push(edge7); + + const edge8: SEdge = { + id: '6-7', + type: 'edge', + sourceId: '6', + targetId: '7', + routerKind: 'manhattan', + }; + edges.push(edge8); + + const edge9: SEdge = { + id: '7-8', + type: 'edge', + sourceId: '7', + targetId: '8', + routerKind: 'manhattan', + children: [{ + id: '7-8_label', + text: 'No', + type: 'label:edge', + edgePlacement: { + position: 0.5, + side: 'on', + rotate: false + } + }] + }; + edges.push(edge9); + + const edge10: SEdge = { + id: '8-7', + type: 'edge', + sourceId: '8', + targetId: '7', + routerKind: 'manhattan', + routingPoints: [{ x: 670.5, y: 721.7 }, { x: 670.5, y: 539.75 }], + }; + edges.push(edge10); + + const edge11: SEdge = { + id: '7-9', + type: 'edge', + sourceId: '7', + targetId: '9', + routerKind: 'manhattan', + children: [{ + id: '7-9_label', + text: 'Yes', + type: 'label:edge', + edgePlacement: { + position: 0.5, + side: 'on', + rotate: false + } + }] + }; + edges.push(edge11); + + const edge12: SEdge = { + id: '9-10', + type: 'edge', + sourceId: '9', + targetId: '10', + routerKind: 'manhattan', + }; + edges.push(edge12); + + const edge13: SEdge = { + id: '10-11', + type: 'edge', + sourceId: '10', + targetId: '11', + routerKind: 'manhattan', + children: [{ + id: '10-11_label', + text: 'No', + type: 'label:edge', + edgePlacement: { + position: 0.5, + side: 'on', + rotate: false + } + }] + }; + edges.push(edge13); + + const edge14: SEdge = { + id: '11-12', + type: 'edge', + sourceId: '11', + targetId: '12', + routerKind: 'manhattan', + children: [{ + id: '11-12_label', + text: 'No', + type: 'label:edge', + edgePlacement: { + position: 0.5, + side: 'on', + rotate: false + } + }] + }; + edges.push(edge14); + + const edge15: SEdge = { + id: '12-13', + type: 'edge', + sourceId: '12', + targetId: '13', + routerKind: 'manhattan', + }; + edges.push(edge15); + + const edge16: SEdge = { + id: '10-14', + type: 'edge', + sourceId: '10', + targetId: '14', + routerKind: 'manhattan', + children: [{ + id: '10-14_label', + text: 'Yes', + type: 'label:edge', + edgePlacement: { + position: 0.5, + side: 'on', + rotate: false + } + }] + }; + edges.push(edge16); + + const edge17: SEdge = { + id: '14-11', + type: 'edge', + sourceId: '14', + targetId: '11', + routerKind: 'manhattan', + routingPoints: [{ x: 1498.3, y: 831.7 }, { x: 1180.15, y: 831.7 }], + }; + edges.push(edge17); + + const edge18: SEdge = { + id: '11-15', + type: 'edge', + sourceId: '11', + targetId: '15', + routerKind: 'manhattan', + children: [{ + id: '11-15_label', + text: 'Yes', + type: 'label:edge', + edgePlacement: { + position: 0.5, + side: 'on', + rotate: false + } + }] + }; + edges.push(edge18); + + const edge19: SEdge = { + id: '15-12', + type: 'edge', + sourceId: '15', + targetId: '12', + routerKind: 'manhattan', + routingPoints: [{ x: 1498.3, y: 1051.7 }, { x: 1180.15, y: 1051.7 }], + }; + edges.push(edge19); + + const graph: SGraph = { + id: 'graph', + type: 'graph', + children: [...nodes, ...edges], + }; + + return graph; +} diff --git a/examples/flowchart/src/di.config.ts b/examples/flowchart/src/di.config.ts new file mode 100644 index 00000000..4221e8ae --- /dev/null +++ b/examples/flowchart/src/di.config.ts @@ -0,0 +1,76 @@ +/******************************************************************************** + * Copyright (c) 2024 TypeFox and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ + +import { Container, ContainerModule } from "inversify"; +import { + CircularNodeView, + ConsoleLogger, + LogLevel, + SCompartmentImpl, + SCompartmentView, + SEdgeImpl, + SGraphImpl, + SGraphView, + SLabelImpl, + SLabelView, + SNodeImpl, + SPortImpl, + SRoutingHandleImpl, + SRoutingHandleView, + TYPES, + configureModelElement, + configureViewerOptions, + editLabelFeature, + loadDefaultModules, + moveFeature, + selectFeature +} from "sprotty"; +import { FlowchartModelSource } from "./model-source"; +import { DecisionNodeView, EdgeLabel, EdgeWithArrow, ProcessNodeView, TerminalNodeView } from "./views"; + +export default (containerId: string) => { + require('../css/diagram.css'); + + const flowchartModule = new ContainerModule((bind, unbind, isBound, rebind) => { + bind(TYPES.ModelSource).to(FlowchartModelSource).inSingletonScope(); + rebind(TYPES.ILogger).to(ConsoleLogger).inSingletonScope(); + rebind(TYPES.LogLevel).toConstantValue(LogLevel.log); + + const context = { bind, unbind, isBound, rebind }; + + configureModelElement(context, 'graph', SGraphImpl, SGraphView); + configureModelElement(context, 'node:terminal', SNodeImpl, TerminalNodeView); + configureModelElement(context, 'node:process', SNodeImpl, ProcessNodeView); + configureModelElement(context, 'node:decision', SNodeImpl, DecisionNodeView); + configureModelElement(context, 'label', SLabelImpl, SLabelView, {enable: [editLabelFeature]}); + configureModelElement(context, 'label:edge', SLabelImpl, EdgeLabel, {enable: [moveFeature, selectFeature, editLabelFeature]}); + configureModelElement(context, 'edge', SEdgeImpl, EdgeWithArrow); + configureModelElement(context, 'routing-point', SRoutingHandleImpl, SRoutingHandleView); + configureModelElement(context, 'volatile-routing-point', SRoutingHandleImpl, SRoutingHandleView); + configureModelElement(context, 'port:in', SPortImpl, CircularNodeView); + configureModelElement(context, 'port:out', SPortImpl, CircularNodeView); + configureModelElement(context, 'compartment', SCompartmentImpl, SCompartmentView); + + configureViewerOptions(context, { + needsClientLayout: true, + }); + }); + + const container = new Container(); + loadDefaultModules(container); + container.load(flowchartModule); + return container; +}; diff --git a/examples/flowchart/src/model-source.ts b/examples/flowchart/src/model-source.ts new file mode 100644 index 00000000..44ea5892 --- /dev/null +++ b/examples/flowchart/src/model-source.ts @@ -0,0 +1,27 @@ +/******************************************************************************** + * Copyright (c) 2024 TypeFox and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ + +import { injectable } from "inversify"; +import { LocalModelSource } from "sprotty"; +import { initializeModel } from "./data"; + +@injectable() +export class FlowchartModelSource extends LocalModelSource { + constructor() { + super(); + this.currentRoot = initializeModel(); + } +} diff --git a/examples/flowchart/src/standalone.ts b/examples/flowchart/src/standalone.ts new file mode 100644 index 00000000..3f1f8329 --- /dev/null +++ b/examples/flowchart/src/standalone.ts @@ -0,0 +1,38 @@ +/******************************************************************************** + * Copyright (c) 2024 TypeFox and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ + +import { LocalModelSource, TYPES } from 'sprotty'; +import { BringToFrontAction, FitToScreenAction } from 'sprotty-protocol'; +import createContainer from './di.config'; + +export default async function runFlowchart() { + const container = createContainer('sprotty'); + const modelSource = container.get(TYPES.ModelSource); + await modelSource.updateModel(); + + const bringToFrontAction: BringToFrontAction = { + kind: 'bringToFront', + elementIDs: ['2-4', '10-11', '11-12'] + }; + + const fitToScreenAction: FitToScreenAction = { + kind: 'fit', + animate: true, + elementIds: [], + padding: 20 + }; + await modelSource.actionDispatcher.dispatchAll([fitToScreenAction, bringToFrontAction]); +} diff --git a/examples/flowchart/src/views.tsx b/examples/flowchart/src/views.tsx new file mode 100644 index 00000000..87407638 --- /dev/null +++ b/examples/flowchart/src/views.tsx @@ -0,0 +1,118 @@ +/******************************************************************************** + * Copyright (c) 2024 TypeFox and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +/** @jsx svg */ +import { PolylineEdgeView, SEdgeImpl, SLabelImpl, isEdgeLayoutable, setAttr, svg } from 'sprotty'; + +import { injectable } from "inversify"; +import { VNode } from "snabbdom"; +import { IViewArgs, RenderingContext, SShapeElementImpl, ShapeView } from "sprotty"; +import { Hoverable, Point, Selectable, getSubType, toDegrees } from "sprotty-protocol"; + +@injectable() +export class TerminalNodeView extends ShapeView { + override render(node: Readonly, context: RenderingContext, args?: IViewArgs): VNode | undefined { + if (!this.isVisible(node, context)) { + return undefined; + } + + return + + {/* {label.text} */} + {context.renderChildren(node)} + ; + } +} + +@injectable() +export class ProcessNodeView extends ShapeView { + override render(node: Readonly, context: RenderingContext, args?: IViewArgs): VNode | undefined { + if (!this.isVisible(node, context)) { + return undefined; + } + + return + + {context.renderChildren(node)} + ; + } +} + +@injectable() +export class DecisionNodeView extends ShapeView { + override render(node: Readonly, context: RenderingContext, args?: IViewArgs): VNode | undefined { + const width = node.size.width; + const height = node.size.height; + + if (!this.isVisible(node, context)) { + return undefined; + } + + return + + {context.renderChildren(node)} + ; + } +} + +@injectable() +export class EdgeLabel extends ShapeView { + override render(label: Readonly, context: RenderingContext, args?: IViewArgs | undefined): VNode | undefined { + if (!isEdgeLayoutable(label) && !this.isVisible(label, context)) { + return undefined; + } + const vnode = {label.text}; + const subType = getSubType(label); + if (subType) { + setAttr(vnode, 'class', subType); + } + + return + + {vnode} + ; + } +} + +@injectable() +export class EdgeWithArrow extends PolylineEdgeView { + + protected override renderAdditionals(edge: SEdgeImpl, segments: Point[], context: RenderingContext): VNode[] { + const p1 = segments[segments.length - 1]; + const p2 = segments[segments.length - 2]; + + return [ + + ]; + } + + angle(x0: Point, x1: Point): number { + return toDegrees(Math.atan2(x1.y - x0.y, x1.x - x0.x)); + } +} diff --git a/examples/index.html b/examples/index.html index 1d9e2a54..0a418e45 100644 --- a/examples/index.html +++ b/examples/index.html @@ -28,6 +28,8 @@

Without Server

  • Multicore:
    Shows a multi-core chip. The color of the cores changes with the code they execute. If you zoom in, the communication channels between the cores appear.
  • +
  • Flowchart:
    + A flowchart with custom views for nodes and edges. The labels on nodes and edges are editable and the nodes and edges are moveable.
  • With Server