Diagram

Diagram connects cards across ordered columns. It owns layout, panning, zoom, and disclosure. The parent supplies nodes, connections, and labels.

import Diagram from "@haloid/azucar/ui/diagram";
import type { DiagramNodeData, DiagramEdgeData, DiagramStage } from "@haloid/azucar/ui/diagram/types";

Data

stages defines column order. Each node names its stage with stage. Each edge connects two node IDs. IDs must be unique within each collection. Node order within a column follows the supplied array. Connections must point to a later stage. The diagram ignores missing endpoints, backward connections, and nodes without a known stage.

This example joins two branches at one shared node. Each card has separate controls for details and connections.

<Diagram
className="h-140 w-full"
ariaLabel="Two branches joining a shared destination"
stages={[
  { id: "first", label: "First" },
  { id: "second", label: "Second" },
  { id: "third", label: "Third" },
]}
nodes={[
  { id: "a", stage: "first", label: "Step A", description: "Branch one", details: [{ label: "Value", value: 12 }] },
  { id: "b", stage: "first", label: "Step B", description: "Branch two", details: [{ label: "Value", value: 24 }] },
  { id: "c", stage: "second", label: "Step C", description: "Shared step", details: [{ label: "Value", value: 36 }] },
  { id: "d", stage: "third", label: "Step D", description: "Destination", details: [{ label: "Value", value: 48 }] },
]}
edges={[
  { id: "ac", source: "a", target: "c" },
  { id: "bc", source: "b", target: "c" },
  { id: "cd", source: "c", target: "d" },
]}
/>

Sizing

The parent sets the height and width. Use className="h-140 w-full" for a contained diagram. Nodes use the existing Card shell. Their heights follow their content. Lines follow measured card heights, including detail transitions. The canvas fades to the page background at its top and bottom edges.

Use FullBleed with interactive for a diagram that reaches both viewport edges. Give the wrapper a height and the diagram h-full. Pass fillViewport to fill the remaining viewport height. The initial view and Fit view center within the canvas.

import FullBleed from "@haloid/azucar/full-bleed";
<FullBleed className="min-h-96" interactive fillViewport>
<Diagram
stages={stages}
nodes={nodes}
edges={edges}
ariaLabel="Connections across six stages"
className="h-full"
/>
</FullBleed>

Disclosure

The header chevron opens a card's details. The connection control shows or hides its outgoing branches. Cards and connections animate together from measured content sizes. Disclosure does not remeasure the animated cards on each frame. Both controls work independently. Shared nodes remain visible when another open branch reaches them. Closing a branch removes its connections, even when their destination stays visible.

Branches start open and details start closed. Set defaultCollapsedIds or defaultDetailsOpen to change the starting state. The controls below the canvas can change all branches or all details. Hidden nodes keep their disclosure state until they return. Use a new React key when switching datasets to reset state.

Path highlight

Hover a card to highlight that card and its incoming connections to the left. The highlight follows every visible upstream branch. Other cards and connections fade. Closed connections stay outside the highlight, even when their nodes remain visible through another branch. Hover changes opacity and stroke. It preserves card positions and zoom.

Keyboard focus on a card control also highlights the path. The highlight clears when neither hover nor keyboard focus remains. Canvas dragging suspends the highlight. Touch input keeps the normal card appearance. Highlight transitions respect reduced motion.

Navigation

Drag the canvas to pan. Cards remain in their columns. Horizontal trackpad scrolling and Shift with the mouse wheel pan horizontally. Vertical scrolling still scrolls the page. Pinch to zoom, or use Control with the mouse wheel. Zoom keeps the point beneath the pointer in place. The initial view fits the visible graph and centers it within the canvas. Fit view restores this view after panning or zooming. Set fitWidthRatio={0.8} to limit the fitted graph to 80% of the canvas width. Tall graphs can fit smaller. The default is 1. Manual zoom can exceed this fitted width. Use the zoom controls to enlarge labels. Pan to follow connections across a large graph. Interaction stops automatic fitting, so opening details does not change the zoom.

Focus the canvas to use arrow keys for panning. Shift increases the distance. Plus and minus change zoom. Home or zero fits the view. Tab reaches the card controls. Enter and Space activate them. Detail transitions respect reduced motion.

Empty state

An empty graph displays No connections to show. Pass emptyState to supply another message through Azucar components.

API

interface DiagramNodeData {
id: string;
stage: string;
label: string;
description?: string;
details?: readonly { label: string; value: string | number }[];
}
interface DiagramEdgeData {
id: string;
source: string;
target: string;
}
interface DiagramProps {
stages: readonly { id: string; label: string }[];
nodes: readonly DiagramNodeData[];
edges: readonly DiagramEdgeData[];
ariaLabel: string;
defaultCollapsedIds?: readonly string[];
defaultDetailsOpen?: boolean;
fitWidthRatio?: number;
emptyState?: React.ReactNode;
className?: string;
}

Use in north

Power Mapping supplies six stages, from generator through UPS, panel, breaker, space, and circuit. Its fitted width uses 85% of the viewport, within the available height. North owns facility and hall selection. Changing either scope resets the diagram. The current topology uses example data. It does not represent live electrical state.