-
Notifications
You must be signed in to change notification settings - Fork 84
Getting Started
The main steps to obtain a Sprotty diagram are the following:
- Define your model by creating subclasses of
SModelElement
. - Define the SVG to generate for each type of model element by implementing views.
- Configure the diagram through dependency injection.
- Connect to a model source, either local or remote
Sprotty comes with a set of model classes that you can reuse for your application, e.g. SNode
and SEdge
for graphs and SChildElement
for other views. However, it is often necessary to add application-specific properties to model elements, so their graphical views can be parameterized. For example, the following code defines a schema interface and a model class:
interface TaskNodeSchema extends SNodeSchema {
name?: string
status?: string
}
class TaskNode extends SNode {
name: string = ''
status: string = ''
}
The TaskNodeSchema is used to feed model contents into the Sprotty application, while the TaskNode represents the elements inside the application. You can skip the schema type if the models are created on the server (in that case you need a server-side representation of the JSON schema).
A view maps a model element to its graphical representation. In the following example we use the JSX syntax to create an SVG group with a circle
and a text
element.
@injectable()
export class TaskNodeView implements IView {
render(node: Readonly<TaskNode>, context: RenderingContext): VNode {
const radius = 20;
// In this context, the coordinates (0,0) mark the upper left corner of
// the node, thus we shift all elements by the radius of the circle.
return <g>
<circle class-sprotty-node={true} class-task={true}
class-running={node.status === 'running'}
class-finished={node.status === 'finished'}
r={radius} cx={radius} cy={radius}></circle>
<text x={radius} y={radius + 5}>{node.name}</text>
</g>;
}
}
The SVG elements are styled with CSS:
.sprotty-node.task {
fill: #c0e0fc;
stroke: #444;
stroke-width: 1;
}
.sprotty-node.task.running {
fill: #f00;
}
.sprotty-node.task.finished {
fill: #0f0;
}
text {
stroke-width: 0;
stroke: #000;
fill: #000;
font-family: sans-serif;
font-size: 11pt;
text-anchor: middle;
}
As described in Dependency Injection, the configuration of a Sprotty application is done using InversifyJS. We recommend to define your InversifyJS Container in a file named di.config.ts
, which could look like this:
const flowModule = new ContainerModule((bind, unbind, isBound, rebind) => {
rebind(TYPES.IModelFactory).to(SGraphFactory).inSingletonScope();
const context = { bind, unbind, isBound, rebind };
configureModelElement(context, 'graph', SGraph, SGraphView);
configureModelElement(context, 'task', TaskNode, TaskNodeView);
configureModelElement(context, 'edge', SEdge, PolylineEdgeView);
});
const container = new Container();
container.load(defaultModule, selectModule, moveModule, boundsModule, undoRedoModule, viewportModule,
exportModule, updateModule, graphModule, routingModule, flowModule);
export default container;
With container.load(...)
you can include all Sprotty modules that are required by your application. Then the view classes are assigned to model element types using the ViewRegistry.
Sprotty supports two kinds of model sources:
- LocalModelSource allows to create models directly in TypeScript or JavaScript.
- WebSocketDiagramServer delegates to a remote source that is connected via web socket.
In this guide we consider the local variant. Add the following line to your ContainerModule (named flowModule
in the example configuration above) in order to enable the model source:
bind(TYPES.ModelSource).to(LocalModelSource).inSingletonScope()
Afterwards you can use the LocalModelSource to initialize and update the model. For example, the following code creates a graph with two task nodes connected with an edge:
const graph: SGraphSchema = {
type: 'graph',
id: 'root',
children: [
{
type: 'task',
id: 'task01',
name: 'First Task',
status: 'finished'
} as TaskNodeSchema,
{
type: 'task',
id: 'task02',
name: 'Second Task',
status: 'running'
} as TaskNodeSchema,
{
type: 'edge',
id: 'edge01',
sourceId: 'task01',
targetId: 'task02'
} as SEdgeSchema
]
}
const modelSource = container.get<LocalModelSource>(TYPES.ModelSource)
modelSource.setModel(graph)