Setting Defaults for new Items
IGraph’s creation methods for elements allow you to create elements without explicitly specifying initial properties, such as a style or an initial geometry. If an initial value is not explicitly specified, a default value defined on IGraph’s nodeDefaults or edgeDefaults properties is used.
Graph Item | Property | Type |
---|---|---|
Note that the defaults for labels and ports are not defined in a property of IGraph but in properties of the INodeDefaults and IEdgeDefaults, respectively. This allows different default configurations for node and edge labels and ports. Similarly, the defaults for port labels are defined in the IPortDefaults, which themselves are defined in the defaults of the port’s owner.
// Get the node defaults
const nodeDefaults = graph.nodeDefaults
// define a new default style
const style = new ShapeNodeStyle({
fill: Color.ORANGE,
shape: ShapeNodeShape.ELLIPSE
})
// set a new default style
nodeDefaults.style = style
// set a new default size
nodeDefaults.size = new Size(40, 40)
// set a new default label model parameter
nodeDefaults.labels.layoutParameter = ExteriorNodeLabelModel.BOTTOM
Changing the defaults only affects items that are created after the change. All items created before the change are not altered.
If no style is provided upon creation the IGraph implementations delegate to the getStyleInstance method to get the style instance to use for the newly created item. Depending on the value of shareStyleInstance, this method either returns the style instance provided by style or a clone of it.
- shareStyleInstance
- If set to
true
, the same style instance is used as the style for each newly created element. Otherwise, a clone of the default style is used. The default istrue
. - getStyleInstance(): INodeStyle
- This method is called to return a default style instance for a newly created graph item. Depending on the value of shareStyleInstance, this is either the instance provided by style or a clone of it.
const defaultStyle = new ShapeNodeStyle()
graph.nodeDefaults.style = defaultStyle
// Share style instances
graph.nodeDefaults.shareStyleInstance = true
const n1 = graph.createNodeAt(new Point(0, 0))
const n2 = graph.createNodeAt(new Point(0, 50))
// At this point, n1.Style and n2.Style are the exact same instance as defaultStyle
defaultStyle.fill = Color.RED
graph.invalidateDisplays() // n1 and n2 both turn red, too
// Clone style instances
graph.nodeDefaults.shareStyleInstance = false
const n3 = graph.createNodeAt(new Point(0, 100))
const n4 = graph.createNodeAt(new Point(0, 150))
// At this point, n3.Style, n4.Style, and defaultStyle are all different instances
defaultStyle.fill = Color.YELLOW // n3 and n4 don't change their color
Note that although shown for INodeDefaults, the methods above are also defined on IEdgeDefaults, ILabelDefaults, and IPortDefaults, too.
It is possible to set a new INodeDefaults instance here, as well. That way, it is possible to share the same defaults for group nodes and normal nodes:
// Use the same defaults for nodes and group nodes
graph.groupNodeDefaults = graph.nodeDefaults
graph.nodeDefaults.style = new ShapeNodeStyle({
shape: 'ellipse',
fill: 'yellow'
})
or to share only the label defaults:
// Use different defaults for nodes and group nodes ...
graph.nodeDefaults.style = new ShapeNodeStyle({
shape: 'ellipse',
fill: 'yellow'
})
graph.groupNodeDefaults.style = new GroupNodeStyle({
tabFill: 'light-blue'
})
// ... but use the same label defaults
graph.groupNodeDefaults.labels = graph.nodeDefaults.labels
graph.nodeDefaults.labels.style = new LabelStyle({
backgroundFill: 'dark-gray'
})
graph.nodeDefaults.labels.layoutParameter = ExteriorNodeLabelModel.BOTTOM
or to keep pre-configured defaults:
const darkTheme = new NodeDefaults({
labels: new LabelDefaults({
style: new LabelStyle({
backgroundFill: 'black',
backgroundStroke: 'white',
textFill: 'light-gray'
})
}),
style: new ShapeNodeStyle({ fill: 'black', stroke: 'white' })
})
const classicTheme = new NodeDefaults({
labels: new LabelDefaults({
style: new LabelStyle({
backgroundFill: 'white',
backgroundStroke: 'black',
textFill: 'black'
})
}),
style: new ShapeNodeStyle({ fill: 'orange' })
})
graph.nodeDefaults = useDarkTheme ? darkTheme : classicTheme
The IPortDefaults.autoCleanUp property plays a special role in that it controls a behavior rather than providing a default value:
- autoCleanUp
- If set to
true
, a port is automatically removed when the last edge connected to it is removed. This means that ports can be implicitly removed. The default istrue
.
const node1 = graph.createNode()
const node2 = graph.createNode()
const port11 = graph.addPort(node1)
const port12 = graph.addPort(node1)
const port2 = graph.addPort(node2)
const edge1 = graph.createEdge(port11, port2)
const edge2 = graph.createEdge(port12, port2)
// enable auto cleanup
graph.nodeDefaults.ports.autoCleanUp = true
graph.remove(edge1)
// port11 is removed with the edge
console.log(graph.contains(port11)) // false
// port2 is not removed since edge2 still is linked to it
console.log(graph.contains(port2)) // true
// disable auto cleanup
graph.nodeDefaults.ports.autoCleanUp = false
graph.remove(edge2)
// no port is removed with the edge now
console.log(graph.contains(port12)) // true
console.log(graph.contains(port2)) // true