Ports
This chapter mainly introduces knowledge related to ports. By reading, you can learn about
- How to configure ports in nodes
- Adding, deleting, and modifying ports
- How to configure port positions
- How to configure label positions on ports
Configuring Connection Ports
First, we group connection ports that have the same behavior and appearance into the same group, and set the grouping using the groups option, which is an object { [groupName: string]: PortGroupMetadata }. The group name is the key, and the value is the default options for each group of connection ports. The supported options are as follows:
interface PortGroupMetadata {
markup?: Markup // Definition of the connection port DOM structure.
attrs?: Attr.CellAttrs // Attributes and styles.
zIndex?: number | 'auto' // The DOM layer level of the connection port; the higher the value, the higher the level.
// Layout of connection ports in the group.
position?: [number, number] | string | { name: string; args?: object }
label?: {
// Connection port label
markup?: Markup
position?: {
// Layout of the connection port label
name: string // Layout name
args?: object // Layout parameters
}
}
}
Next, we configure items, which is an array PortMetadata[]. Each item in the array represents a connection port, and the supported options for connection ports are as follows:
interface PortMetadata {
id?: string // Unique ID for the connection port, automatically generated by default.
group?: string // Group name; specifying a group will inherit the options of the connection ports in that group.
args?: object // Parameters for the layout algorithm specified for the connection ports in the group. We cannot specify a layout algorithm for a single connection port, but we can provide different parameters for the layout algorithm specified for the group.
markup?: Markup // Definition of the connection port's DOM structure. Specifying this option will override the default options provided by the group referred to by `group`.
attrs?: Attr.CellAttrs // Element's attribute styles. Specifying this option will override the default options provided by the group referred to by `group`.
zIndex?: number | 'auto' // The DOM layer level of the connection port; the higher the value, the higher the level. Specifying this option will override the default options provided by the group referred to by `group`.
label?: {
// Label for the connection port. Specifying this option will override the default options provided by the group referred to by `group`.
markup?: Markup // Label DOM structure
position?: {
// Label position
name: string // Name of the label position calculation method
args?: object // Parameters for the label position calculation method
}
}
}
The following example code clearly shows how to define connection ports.
import './index.less'
import { createRoot } from 'react-dom/client'
import React from 'react'
import { Graph } from '@antv/x6'
Graph.registerNode(
'custom-node-with-port',
{
inherit: 'rect',
width: 100,
height: 40,
attrs: {
body: {
stroke: '#8f8f8f',
strokeWidth: 1,
fill: '#fff',
rx: 6,
ry: 6,
},
},
ports: {
groups: {
top: {
position: 'top',
attrs: {
circle: {
magnet: true,
stroke: '#8f8f8f',
r: 5,
},
},
},
bottom: {
position: 'bottom',
attrs: {
circle: {
magnet: true,
stroke: '#8f8f8f',
r: 5,
},
},
},
},
},
},
true,
)
export default class Example extends React.Component {
private container: HTMLDivElement
componentDidMount() {
const graph = new Graph({
container: this.container,
background: {
color: '#F2F7FA',
},
})
const source = graph.addNode({
shape: 'custom-node-with-port',
x: 40,
y: 40,
label: 'hello',
ports: {
items: [
{
id: 'port_1',
group: 'bottom',
},
{
id: 'port_2',
group: 'bottom',
},
],
},
})
const target = graph.addNode({
shape: 'custom-node-with-port',
x: 160,
y: 180,
label: 'world',
ports: {
items: [
{
id: 'port_3',
group: 'top',
},
{
id: 'port_4',
group: 'top',
},
],
},
})
graph.addEdge({
source: { cell: source, port: 'port_2' },
target: { cell: target, port: 'port_3' },
attrs: {
line: {
stroke: '#8f8f8f',
strokeWidth: 1,
},
},
})
graph.centerContent()
}
refContainer = (container: HTMLDivElement) => {
this.container = container
}
render() {
return (
<div className="config-app">
<div className="app-content" ref={this.refContainer} />
</div>
)
}
}
const root = createRoot(document.getElementById('container')!)
root.render(<Example />)
window.addEventListener('pagehide', () => root.unmount(), { once: true })
.config-app {
display: flex;
width: 100%;
border: 1px solid #f0f0f0;
padding: 16px 8px;
font-family: sans-serif;
.app-content {
flex: 1;
height: 280px;
margin-right: 8px;
margin-left: 8px;
border-radius: 5px;
box-shadow: 0 12px 5px -10px rgb(0 0 0 / 10%), 0 0 4px 0 rgb(0 0 0 / 10%);
}
}Modifying Connection Ports
There is a rich API for adding, deleting, and modifying connection ports on nodes.
// Add a connection port
node.addPort({
group: 'top',
attrs: {
text: {
text: 'xx',
},
},
})
// Remove a connection port
node.removePort(portId)
// Update a connection port
node.portProp(portId, 'attrs/circle/stroke', color)
import './index.less'
import { createRoot } from 'react-dom/client'
import React from 'react'
import { Graph, Node, Color } from '@antv/x6'
import { Button } from 'antd'
Graph.registerNode(
'custom-node-with-port',
{
inherit: 'rect',
width: 100,
height: 40,
attrs: {
body: {
stroke: '#8f8f8f',
strokeWidth: 1,
fill: '#fff',
rx: 6,
ry: 6,
},
},
ports: {
groups: {
top: {
position: 'top',
attrs: {
circle: {
magnet: true,
stroke: '#8f8f8f',
r: 5,
},
},
label: {
position: 'top',
},
},
bottom: {
position: 'bottom',
attrs: {
circle: {
magnet: true,
stroke: '#8f8f8f',
r: 5,
},
},
label: {
position: 'top',
},
},
},
},
},
true,
)
const commands = [
{
key: 'addPort',
label: 'addPort',
},
{
key: 'removePort',
label: 'removePort',
},
{
key: 'updatePort',
label: 'updatePort',
},
]
export default class Example extends React.Component {
private container: HTMLDivElement
private graph: Graph
componentDidMount() {
const graph = new Graph({
container: this.container,
background: {
color: '#F2F7FA',
},
})
graph.addNode({
id: '1',
shape: 'custom-node-with-port',
x: 150,
y: 100,
label: 'hello',
})
graph.centerContent()
this.graph = graph
}
change = (command: string) => {
const node = this.graph.getCellById('1') as Node
const ports = node.getPorts()
let color: string
switch (command) {
case 'addPort':
node.addPort({
group: 'top',
attrs: {
text: {
text: `${ports.length + 1}`,
},
},
})
break
case 'removePort':
if (ports.length) {
node.removePortAt(ports.length - 1)
}
break
case 'updatePort':
color = Color.randomHex()
ports.forEach((port) => {
node.portProp(port.id!, 'attrs/circle/stroke', color)
})
break
default:
break
}
}
refContainer = (container: HTMLDivElement) => {
this.container = container
}
render() {
return (
<div className="dynamic-app">
<div className="app-btns">
<Button.Group>
{commands.map((item) => (
<Button onClick={() => this.change(item.key)} key={item.key}>
{item.label}
</Button>
))}
</Button.Group>
</div>
<div className="app-content" ref={this.refContainer} />
</div>
)
}
}
const root = createRoot(document.getElementById('container')!)
root.render(<Example />)
window.addEventListener('pagehide', () => root.unmount(), { once: true })
.dynamic-app {
border: 1px solid #f0f0f0;
padding: 16px 8px;
font-family: sans-serif;
.app-btns {
bottom: 0;
margin: 0;
padding: 0 8px;
}
.app-content {
width: 100%;
height: 240px;
margin: 0;
margin-top: 16px;
border-radius: 5px;
box-shadow: 0 12px 5px -10px rgb(0 0 0 / 10%), 0 0 4px 0 rgb(0 0 0 / 10%);
}
}Connection Port Position
The layout algorithm for connection ports can only be specified through the position option in groups, as the layout algorithm needs to consider all connection ports in the group when calculating their positions. We can influence the layout result of a single connection port through the args option.
We provide the following layout algorithms for connection ports by default, and also support custom connection port layout algorithms and registration. Click the links below to learn how to use each layout algorithm.
absoluteAbsolute positioning.leftEvenly distributed on the left side of rectangular nodes.rightEvenly distributed on the right side of rectangular nodes.topEvenly distributed on the top of rectangular nodes.bottomEvenly distributed on the bottom of rectangular nodes.lineEvenly distributed along a specified line.ellipseDistributed along an elliptical arc.ellipseSpreadEvenly distributed along an ellipse.
Connection Port Label Position
The label position can be specified in groups.label.position and in the node’s ports.items.label.position option.
We provide the following label positions by default, and also support custom label positions and registration. Click the links below to learn how to use each label position.
leftThe label is located on the left side of the connection port.rightThe label is located on the right side of the connection port.topThe label is located above the connection port.bottomThe label is located below the connection port.insideThe label is located inside the node (close to the edge).outsideThe label is located outside the node (close to the edge).insideOrientedThe label is located inside the node and automatically adjusts the text direction based on its position.outsideOrientedThe label is located outside the node and automatically adjusts the text direction based on its position.radialThe label is located outside circular or elliptical nodes.radialOrientedThe label is located outside circular or elliptical nodes and the label text automatically rotates along the arc direction.