Import it from @grafloria/engine.
tsclass LinkModel extends DiagramEntity
Properties
| Name | Type | Default | Description |
|---|---|---|---|
diagram? | DiagramModel | . Back-reference to the owning diagram, set by DiagramModel.installLink. Without this, read-only would have been unenforceable on exactly the edits the interaction controller performs most. NON-ENUMERABLE (defined in the constructor, mirroring NodeModel): an enumerable back-reference to the diagram would make every LinkModel a circular object and blow up deep-clone and JSON serialization. | |
sourcePortId | string | ||
targetPortId | string | ||
sourceNodeId? | string | ||
targetNodeId? | string | ||
pathType | 'direct' | 'orthogonal' | 'smooth' | 'bezier' | 'smooth' | |
router? | LinkRouterName | WHERE the line goes. When unset, derived from pathType — see {@link effectiveRouter}. Setting it does NOT touch pathType, so legacy consumers keep working; the explicit field simply wins. | |
connector? | LinkConnectorName | HOW the polyline is drawn. Unset = derived from pathType. | |
points | Point[] | [] | |
segments | PathSegment[] | [] | |
labels | LinkLabel[] | [] | |
state | 'default' | 'selected' | 'hovered' | 'highlighted' | 'default' | |
style | Partial<LinkStyle> | {} | |
data | Record<string, any> | {} | |
isSourceEndpointSelected | boolean | false | Whether source endpoint handle is selected Used for dragging endpoint to reconnect |
isTargetEndpointSelected | boolean | false | Whether target endpoint handle is selected Used for dragging endpoint to reconnect |
DEFAULT_CURVATURE (static) | 0.5 | The curve tightness of a smooth/bezier link. style.curvature is a multiplier of the endpoint distance for the control-point offset. It used to be DEAD (declared on LinkStyle, read by nobody); it is now the single knob both this model and the SVG renderer read, so a per-link value produces the same curve whichever produced the path. Default 0.5 = the historical hardcoded factor; negatives are cla |
Methods
constructor( sourcePortId: string, targetPortId: string, pathType?: 'direct' | 'orthogonal' | 'smooth' | 'bezier' )isSelfLoop(): boolean— Is this link a SELF-LOOP, i.e. do both ends live on the same node?
Reads the cached owning-node ids, which DiagramModel.installLink backfills
for every link that reaches a diagram (including ones built with
new LinkModel() + addLink, which carry no ids of their own). A link that
has never been installed has no ids and is — correctly — not a self-loop as
far as anything can tell.
A self-loop between two DIFFERENT ports of the same node counts, and so does one that starts and ends on the SAME port.
getNodePairKey(): string | null— The unordered node pair this link connects, as a stable key. UNORDERED on purpose: A→B and B→A are the same visual bundle and must fan out together, or a bidirectional pair would draw both links on the same centre line. Returns null when the owning nodes are unknown.setSourcePort(portId: string, nodeId?: string): void— Set source portsetTargetPort(portId: string, nodeId?: string): void— Set target portsetPathType(pathType: 'direct' | 'orthogonal' | 'smooth' | 'bezier'): void— Set path typesetRouter(router: LinkRouterName | undefined): void— Set the routing geometry explicitly. Clears the cached route the same way setPathType does — the old polyline belongs to the old router.setConnector(connector: LinkConnectorName | undefined): void— Set the polyline rendering explicitly. Pure re-render; the routed points are still valid, so the cache is NOT cleared.effectiveRouter(): LinkRouterName— The router actually in force: the explicit field, else derived from pathType exactly as the renderer always derived it (direct → straight, orthogonal → orthogonal, smooth/bezier → straight-with-curved-rendering).effectiveConnector(): LinkConnectorName— The connector actually in force: the explicit field, else implied by an explicitly axis-aligned ROUTER, else derived from pathType.
The router rung exists because { router: 'orthogonal' } with the default
pathType used to draw a smooth SPLINE through Manhattan waypoints — the
route was orthogonal, the picture was wavy (the screenshot audit caught the
demo's own readout saying "orthogonal" over a curve). Asking for an
axis-aligned router IS asking for axis-aligned rendering; rounded corners
are that family's standard look. An explicit connector still overrides.
setPoints(points: Point[]): void— Set points for custom pathaddPoint(point: Point, index?: number): void— Add point to pathremovePoint(index: number): Point | undefined— Remove point from pathgeneratePath( sourcePoint: Point, targetPoint: Point, sourceDirection?: 'left' | 'right' | 'top' | 'bottom', targetDirection?: 'left' | 'right' | 'top' | 'bottom' ): void— Generate path based on typegetCurvature(): numberaddLabel( label: Partial<LinkLabel> & { text: string } & ( | { position: number } | { slot: NonNullable<LinkLabel['slot']> } ) ): void— Add label
position is no longer required when the label names a
slot — and every other LinkLabel field (html, template, slot, autoOffset,
rotation…) is now carried through instead of being silently dropped. The old
body hand-copied five fields, so a label created here could not be an HTML
label, could not auto-rotate and could not opt into auto-placement.
removeLabel(labelId: string): LinkLabel | undefined— Remove label by IDremoveLabelAt(index: number): LinkLabel | undefined— Remove label by indexupdateLabel(index: number, updates: Partial<LinkLabel>): void— Update label by indexsetLabels(labels: LinkLabel[]): void— REPLACE the whole label collection — the writeaddLabel/updateLabelcannot express.
SetLinkLabelsCommand used to do this by assigning link.labels directly on BOTH
execute and undo. A plain field write does not pass trackChange() — the one funnel
collab captures from — so the command emitted ZERO ops in BOTH directions: authoring
a link's labels was invisible to every other peer, and so was taking it back. (The
UpdateLinkStyleCommand defect at least emitted one op on execute; this emitted none
at all.) replaceStyle is the same seam for the same reason.
-
override getLabel(): string | undefined— Overloaded label read. -
getLabel()— no argument — is the CANONICAL display-label accessor inherited from DiagramEntity (metadata.label, with the legacy data['label'] fallback). It is what the DSL generator exports and what feeds the renderer's simple edge label (svg-renderer reads link.getMetadata('label')). -
getLabel(labelId)is the pre-existing lookup into the POSITIONED multi-label API (labels: LinkLabel[]/ addLabel / updateLabel), a different feature that happens to share the verb. The overload keeps both callable without forking the canonical name. -
override getLabel(labelId: string): LinkLabel | undefined— Overloaded label read. -
getLabel()— no argument — is the CANONICAL display-label accessor inherited from DiagramEntity (metadata.label, with the legacy data['label'] fallback). It is what the DSL generator exports and what feeds the renderer's simple edge label (svg-renderer reads link.getMetadata('label')). -
getLabel(labelId)is the pre-existing lookup into the POSITIONED multi-label API (labels: LinkLabel[]/ addLabel / updateLabel), a different feature that happens to share the verb. The overload keeps both callable without forking the canonical name. -
override getLabel(labelId?: string): string | LinkLabel | undefined— Overloaded label read. -
getLabel()— no argument — is the CANONICAL display-label accessor inherited from DiagramEntity (metadata.label, with the legacy data['label'] fallback). It is what the DSL generator exports and what feeds the renderer's simple edge label (svg-renderer reads link.getMetadata('label')). -
getLabel(labelId)is the pre-existing lookup into the POSITIONED multi-label API (labels: LinkLabel[]/ addLabel / updateLabel), a different feature that happens to share the verb. The overload keeps both callable without forking the canonical name. -
setState(state: 'default' | 'selected' | 'hovered' | 'highlighted'): void— Set state -
updateStyle(style: Partial<LinkStyle>): void— Update style -
replaceStyle(style: Partial<LinkStyle>): void— REPLACE the whole style object — the writeupdateStylecannot express.
updateStyle merges, so it can never REMOVE a key; restoring a snapshot has to
assign wholesale. UpdateLinkStyleCommand.undo() used to do that with a direct
field write (link.style = restored), which never passes trackChange() — the
single funnel collab captures from. Measured: execute emitted 1 op, undo emitted 0,
so every peer kept the styled link forever while the author saw it correctly
reverted. See collab/style-undo.spec.ts.
setData(key: string, value: any): void— Set data propertygetData(key: string): any— Get data propertyreconnectSource(newPortId: string, newNodeId?: string): void— Reconnect source endpoint to new port Used for link reconnection workflowreconnectTarget(newPortId: string, newNodeId?: string): void— Reconnect target endpoint to new port Used for link reconnection workflowgetSourceEndpoint(): Point— Get source endpoint position Returns the first point in the path (source end)getTargetEndpoint(): Point— Get target endpoint position Returns the last point in the path (target end)selectSourceEndpoint(): void— Select source endpoint handleselectTargetEndpoint(): void— Select target endpoint handledeselectEndpoints(): void— Deselect all endpoint handleshasSelectedEndpoint(): boolean— Check if any endpoint is selectedgetPointAtPosition(t: number): Point | null— Get point at position along link (0-1)getTotalLength(): number— Get total path lengthgetLength(): number— Get path length (alias for getTotalLength for API consistency)getTangentAt(t: number): Point | null— Get tangent (direction vector) at position along path (0-1) Returns normalized direction vectorgetNormalAt(t: number): Point | null— Get normal (perpendicular vector) at position along path (0-1) Returns normalized perpendicular vector (90° counter-clockwise from tangent)getClosestPoint(point: Point): { point: Point; distance: number; t: number } | null— Get closest point on path to a given point Returns the closest point, distance, and normalized position (t)getAngleAt(t: number): number | null— Get angle at position along path (0-1) in degrees Useful for label rotationoverride serialize(): SerializedLink— Serialize to JSONstatic fromJSON(data: SerializedLink): LinkModel(static) — Deserialize from JSON
Was this page helpful?