Ports express where a link may attach. Grafloria combines port anatomy, declared data types, and custom validators to decide whether a connection is offered.
mermaidflowchart LR S["Source port"] --> A["Anatomy\ndirection and caps"] A --> V["Custom validators"] V --> D["Connection offered"]
Use a declared PortSpec when a node needs explicit direction, a connection cap, or a particular glyph:
html<div id="diagram" style="height: 320px; width: 640px"></div>
tsimport { render } from '@grafloria/element';
const spec = {
nodes: [
{
id: 'transform',
position: { x: 240, y: 120 },
size: { width: 170, height: 90 },
label: 'Transform',
ports: [
{ id: 'in', side: 'left', type: 'input' },
{ id: 'out', side: 'right', type: 'output' },
{
id: 'errors',
side: 'bottom',
type: 'output',
maxConnections: 1,
shape: { shape: 'diamond', size: 12 },
label: { text: 'errors', layout: 'outside' },
},
],
},
],
edges: [],
};
const container = document.getElementById('diagram')!;
container.style.height = '320px';
container.style.width = '640px';
const instance = render(spec, container);
instance.fitView(24);
The mounted diagram shows the Transform node with its declared input, output, and labelled diamond-shaped error port. The errors port accepts one connection; the other declared port has no such cap.
The render() call returns a DiagramInstance, so use the instance for view operations and reach its engine only when you need engine configuration.
The connection checks
1. Anatomy
The port's type controls direction: an input cannot start a wire, and an output cannot receive one. bi ports can do both. Caps control how many links a port accepts. Use maxConnections for the legacy total cap, or gating when you need separate incoming and outgoing caps or directional connectability.
tsconst ports = [
{ id: 'out', side: 'right', type: 'output', gating: { fromMaxLinks: 3 } },
{ id: 'in', side: 'left', type: 'input', gating: { toMaxLinks: 1 } },
];
The engine checks the source port first and the target port second. A full target is highlighted as invalid during a drag, and the connection is not offered.
2. Custom validation
Use registerConnectionValidator for a rule that depends on application data, such as forbidding a connection from reaching a node with the role sink. The validator receives a connection candidate containing both nodes, both ports, and an optional existing link when the user reconnects an edge. Return true to allow the candidate or false (or a reason string) to veto it.
tsimport { render } from '@grafloria/element';
import { registerConnectionValidator } from '@grafloria/renderer';
const container = document.getElementById('diagram')!;
container.style.height = '320px';
container.style.width = '640px';
const instance = render(
{
nodes: [
{
id: 'source',
position: { x: 40, y: 100 },
size: { width: 140, height: 70 },
label: 'Source',
data: { role: 'source' },
ports: [{ id: 'out', side: 'right', type: 'output' }],
},
{
id: 'sink',
position: { x: 300, y: 100 },
size: { width: 140, height: 70 },
label: 'Sink',
data: { role: 'sink' },
ports: [
{ id: 'in', side: 'left', type: 'input' },
],
},
],
edges: [],
},
container,
);
const disposeValidator = registerConnectionValidator(({ targetNode }) => {
return targetNode.getData('role') === 'sink'
? 'A sink cannot receive a connection.'
: true;
});
instance.fitView(24);
The mounted diagram shows Source and Sink. The custom rule vetoes a connection whose target node has the role sink, so dragging from Source to Sink produces no link.
Keep validation scoped to the view
Connection validators are process-global, not per canvas. Keep the returned disposer and call it when the view unmounts. If the application owns the entire registry and needs a clean slate, call clearConnectionValidators as part of teardown. Otherwise a validator can affect another route, a remounted canvas, or a second development render.
In the view's unmount or destroy hook, call disposeValidator() and then
instance.dispose(). Do not run either call immediately after mounting: that
removes the live diagram and its rule.
Was this page helpful?