SVG Shapes
Diagrams use SVG to render shapes. These shapes are stored inside ShapeSets, and when you create a diagram you instruct VisuallyJs which shape sets you want to use.
Registering shapes
Required data
In order for the diagram to be able to pick the appropriate shape for some vertex, the vertex data must contain:
- A
typeproperty that maps to the ID of one of the shapes in your library - A
categoryproperty that maps to some shape set ID - A
widthandheightvalue for each node. The SVG shapes usepathelements internally, which require absolute coordinates, and so they need to know the current size of your vertices. - An
xandyvalue for each node
Optional data
The full list of optional data properties depends on the shape libraries you are using, but all of the VisuallyJs libraries support these optional properties:
fill- Color to fill the shape with. Defaults to#FFFFFF.outline- Color to outline the shape with. Defaults to#000000.outlineWidth- Width of the shape's outline. Defaults to 2px.color- Color to use for the shape's label (if shown). Defaults to#000000.
As an example, consider this list of two nodes:
[
{
"id":"1",
"x":50,
"y":50,
"type":"process",
"category":"flowchart",
"width":100,
"height":100,
"fill":"white",
"outline":"black"
},
{
"id":"2",
"x":150,
"y":250,
"type":"terminus",
"category":"flowchart",
"width":100,
"height":100,
"fill":"white",
"outline":"black"
}
]
This is rendered as:
Cell Template Events
DiagramOptions.cells.templateEvents lets you attach delegated event handlers to elements inside rendered shape templates. Keys are CSS selectors, and each selector maps event names to callbacks.
renderDiagram(container, {
cells: {
templateEvents: {
".delete": {
tap: ({ cell }) => cell.remove()
},
".label": {
dbltap: ({ cell }) => startEditing(cell)
}
}
}
})
You can bind a listener to anything listed as a BindableViewEvent:
Displaying Labels
By default, a diagram will display a label value from each node - if we add label to the vertices from before we'll see them:
[
{
"id":"1",
...,
"label":"Node 1"
},
{
"id":"2",
...,
"label":"Node 2"
}
]
Hiding labels
You can hide labels by setting showLabels:false in your diagram's edges options:
Shape properties
A ShapeType can define a properties array to describe the values its template uses and make those values available in an inspector. Each property has an id, label, and type. The id identifies the value in the shape’s model data. The label may be shown to the user.
Supported property types are:
string— a single-line text value.text— a multi-line text value.number— a numeric value. You can also setminandmax.boolean— a true or false value.color— a color value.
Properties can also include a defaultValue and a description. The default is used when the property has no value in the model; the description can help explain the setting in the inspector.
Value lists
string, text and number properties can be declared with a list of possible values, for example:
{
id:"pump",
label:"Pump",
properties:[
{
id: "pumpType",
label: "Type",
type: "string",
template:"....",
values: ["Centrifugal", "Positive Displacement"],
defaultValue: "Centrifugal"
}
]
}
The items in values may be strings, as shown above, or they can be a Javascript object containing a label and value, for those use cases where your internal values are not very useful as human readable text:
{
id:"pump",
label:"Pump",
properties:[
{
id: "pumpType",
label: "Type",
type: "string",
template:"....",
values: [
{ label:"Centrifugal", value:"c" },
{ label:"Positive Displacement", value:"d" }
],
defaultValue: "c"
}
]
}
Default values
Any property can declare a defaultValue:
{
id:"pump",
label:"Pump",
properties:[
{
id: "pumpType",
label: "Type",
type: "string",
template:"....",
values: ["Centrifugal", "Positive Displacement"],
defaultValue: "Centrifugal"
}
]
}
This will be used in the initial payload for an item of this type when it is added to the dataset.
Read-only properties
Set readOnly: true to display a property's value without an editable control in the inspector. This only affects the inspector UI: it does not make the model value read-only. Application code and other model operations can still write to that property, and the inspector will display the updated value.
Choosing controls with editor
For string and number properties, supply a values array to present a fixed set of choices instead of a free-form input. Values can be plain strings, or objects with separate label and value fields when the text shown to the user should differ from the stored value.
Use editor to choose how those options are presented:
radiodisplays radio buttons.selectdisplays a dropdown.- If omitted, the integration chooses a control based on the number of options. The framework inspectors show radio buttons for up to seven choices by
default, and a dropdown for larger lists. Their
maxRadiossetting can change that threshold.
For a boolean property, editor: "radio" or editor: "select" replaces the default checkbox with radio buttons or a dropdown. To set the displayed
boolean labels, provide a two-item booleanLabels tuple. The first label is used for true, and the second for false; for example:
{
id: "enabled",
label: "Enabled",
type: "boolean",
editor: "select",
booleanLabels: ["On", "Off"]
}
Without booleanLabels, the framework inspectors use “True” and “False” for boolean radio buttons and dropdowns. With the default checkbox, the property's label is shown beside the control.
Shape property inspectors
Each framework integration provides a ShapePropertiesInspector component for rendering the selected shape’s properties. Add it within the integration’s inspector component and pass it the vertex being inspected. The component reads the shape definition from the active shape library and renders the property controls described above.
React
Use the ShapePropertiesInspector component exported by the React integration. It accepts a vertex and an optional maxRadios value, which sets how many choices are shown as radio buttons before the inspector switches to a dropdown.
Vue
Use the registered ShapePropertiesInspectorComponent within the Vue inspector. It accepts the vertex and an optional maxRadios value, with the same radio- button threshold behavior.
Svelte
Use the ShapePropertiesInspector component within the Svelte inspector and pass it the vertex. Its optional maxRadios setting controls when a list of choices is rendered as a dropdown instead of radio buttons.
Angular
Use the vjs-shape-properties-inspector component within the Angular inspector and bind its vertex input. The optional maxRadios input controls when a list of choices is rendered as a dropdown instead of radio buttons.
Vanilla JavaScript
The vanilla ShapeTypeInspector generates inspector controls from the resolved property definitions. It renders strings, text, numbers, booleans, and colors as standard inputs, and displays readOnly values without editable controls. For font properties, it provides inputs for the font size, family, weight, and style.
The vanilla inspector’s built-in template does not use values, editor, or booleanLabels to render choice controls. If you need those controls in vanilla JavaScript, provide a custom inspector template.
Available sets
VisuallyJs ships with two shape sets in the browser-ui package and a set of BPMN shapes:
Flowchart
import { FLOWCHART_SHAPES } from "@visuallyjs/browser-ui"
Basic
import { BASIC_SHAPES } from "@visuallyjs/browser-ui"
BPMN
import { BPMN2_SHAPES } from "@visuallyjs/bpmn"
These shapes are shipped in the @visuallyjs/bpmn library - you'll need to import it.
Custom shape sets
It's straightforward to make your own shape set - they consist of an id and then a list of shapes, for each of which you provide SVG.
Here we have made a set of faces:
const shapes = {
id:"faces",
shapes:[
{
type:"impassive",
template:`<g>
<circle cx="{{width/2}}" cy="{{height/2}}" r="{{(width/2)}}"/>
<path d="M {{width/4}} {{height*3/4}} L {{width*3/4}} {{height*3/4}}"/>
<circle cx="{{width/4}}" cy="{{height/4}}" r="10"/>
<circle cx="{{width*3/4}}" cy="{{height/4}}" r="10"/>
<circle cx="{{width/2}}" cy="{{height/2}}" r="10"/>
</g>`,
label:"Impassive"
},
{
type:"pleased",
template:`<g>
<circle cx="{{width/2}}" cy="{{height/2}}" r="{{(width/2)}}"/>
<circle cx="{{width/4}}" cy="{{height/4}}" r="10"/>
<circle cx="{{width*3/4}}" cy="{{height/4}}" r="10"/>
<circle cx="{{width/2}}" cy="{{height/2}}" r="10"/>
<path d="M {{width/4}} {{height*3/4}} C {{width/4}} {{height*7/8}}, {{width*3/4}} {{height*7/8}} {{width*3/4}} {{height*3/4}}"/>
</g>`,
label:"Pleased"
},
{
type:"notpleased",
template:`<g>
<circle cx="{{width/2}}" cy="{{height/2}}" r="{{(width/2)}}"/>
<circle cx="{{width/4}}" cy="{{height/4}}" r="10"/>
<circle cx="{{width*3/4}}" cy="{{height/4}}" r="10"/>
<circle cx="{{width/2}}" cy="{{height/2}}" r="10"/>
<path d="M {{width/4}} {{height*3/4}} C {{width/4}} {{height*5/8}}, {{width*3/4}} {{height*5/8}} {{width * 0.75}} {{height * 0.75}}"/>
</g>`,
label:"Not Pleased"
}
]
}
Nested Shape sets
It is possible to nest shape sets, via the children property of a shape set. For instance, say we want to split our set of faces into two subsets:
const shapes = {
id:"faces",
children:[
{
id:"expressionless",
name:"Expressionless",
shapes:[
{
id:"impassive",
template:`...`
}
]
},
{
id:"expressions",
name:"Expressions",
shapes:[
{
id:"pleased",
template:`...`
},
{
id:"notpleased",
template:`...`
}
]
}
]
}