Skip to content
This is the alpha v4 version website. Looking for the v3 documentation?

Attachments

Label attachments let you display custom SVG content anchored to a node’s label. This is useful for tooltips, info cards, or interactive UI. The way it works is that, when sigma needs to render an attachment for a given node, it will call a function that will return the content as an SVG string of a Canvas element directly. It will then draw it in a texture, and render it directly with the WebGL engine.

How it works

  1. Declare an attachment renderer in primitives.nodes.labelAttachments. Each renderer is a function that receives context and returns the content.
  2. Assign it to nodes via the labelAttachment style property, typically with a conditional to show it on hover.
  3. Position it with labelAttachmentPlacement.

The renderer function receives a LabelAttachmentContext with attributes, state, and graphState. It returns a LabelAttachmentContent object with type: "svg" and an svg string.

import Graph from "graphology";
import Sigma from "sigma";
import { layerFill, sdfCircle } from "sigma/rendering";
import { DEFAULT_STYLES } from "sigma/types";
import type { LabelAttachmentContent, LabelAttachmentContext } from "sigma/types";
import { registerControls } from "../_controls";
import { nodeExtent } from "graphology-metrics/graph/extent";
const container = document.getElementById("sigma-container") as HTMLElement;
// Used to measure text width so the SVG is sized to fit its content.
const measureCtx = document.createElement("canvas").getContext("2d")!;
function drawInfoCard({ attributes }: LabelAttachmentContext): LabelAttachmentContent {
const tag = attributes.tag as string;
const clusterLabel = attributes.clusterLabel as string;
const color = attributes.color as string;
measureCtx.font = "11px sans-serif";
const width =
Math.ceil(Math.max(measureCtx.measureText(tag).width, measureCtx.measureText(clusterLabel).width)) + 8;
return {
type: "svg",
svg: `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="34">
<text x="4" y="13" font-family="sans-serif" font-size="11" fill="darkgrey">${tag}</text>
<text x="4" y="29" font-family="sans-serif" font-size="11" fill="${color}">${clusterLabel}</text>
</svg>`,
};
}
interface Dataset {
nodes: { key: string; label: string; tag: string; cluster: string; x: number; y: number; score: number }[];
edges: [string, string][];
clusters: { key: string; color: string; clusterLabel: string }[];
}
const res = await fetch("/data/wikipedia.json");
const dataset: Dataset = await res.json();
const clustersByKey = Object.fromEntries(dataset.clusters.map((c) => [c.key, c]));
const graph = new Graph();
dataset.nodes.forEach((node) => {
const cluster = clustersByKey[node.cluster];
graph.addNode(node.key, {
...node,
clusterLabel: cluster?.clusterLabel,
color: cluster?.color,
});
});
dataset.edges.forEach(([source, target]) => {
graph.addEdge(source, target);
});
const [minScore, maxScore] = nodeExtent(graph, "score");
new Sigma(graph, container, {
primitives: {
nodes: {
shapes: [sdfCircle()],
layers: [layerFill()],
labelAttachments: {
hoverInfo: drawInfoCard,
},
},
},
styles: {
nodes: [
DEFAULT_STYLES.nodes,
{
size: {
attribute: "score",
min: 15,
max: 100,
minValue: minScore,
maxValue: maxScore,
},
labelAttachment: {
whenState: "isHovered",
then: "hoverInfo",
},
},
],
},
settings: {
labelRenderedSizeThreshold: 6,
},
});