Graph / DAG Editor
SVG-based node graph with reactive cx/cy/d/viewBox bindings, drag-to-move, drag-to-connect, and auto-layout toggle. Exercises the compiler's SVG namespace path that other blocks don't touch.
#Preview
1"use client"23import { createSignal, createMemo } from '@barefootjs/client'45// Nodes and edges live in plain signals. The SVG view mirrors them6// directly: <circle cx={n.x}/> binds to a node's position, <path d=7// {edgePath(e)}/> rebuilds whenever any node moves, and the root8// <svg viewBox={viewBox()}/> reacts to zoom changes. Every other9// block uses HTML elements only — this is the first block to drive10// the compiler's SVG namespace path with reactive updates.1112function GraphEditor() {13 const [nodes, setNodes] = createSignal<GraphNode[]>(INITIAL_NODES)14 const [edges, setEdges] = createSignal<GraphEdge[]>(INITIAL_EDGES)15 const [zoom, setZoom] = createSignal(1)1617 const viewBox = createMemo(() => {18 const w = 720 / zoom(), h = 400 / zoom()19 return `${360 - w / 2} ${200 - h / 2} ${w} ${h}`20 })2122 function edgePath(e: GraphEdge): string {23 const map = nodeIndex()24 const s = map[e.source], t = map[e.target]25 return bezierPath(s.x, s.y, t.x, t.y)26 }2728 return (29 <svg viewBox={viewBox()} onPointerMove={onMove} onPointerUp={onUp}>30 {/* Edges: d rebuilds on any node move — mapArray over signal-derived list. */}31 <g>32 {edges().map(e => (33 <path key={e.id} d={edgePath(e)} stroke="#94a3b8" fill="none"/>34 ))}35 </g>3637 {/* Nodes: cx/cy on <circle>, x/y on <text>, plus a connect handle.38 Nested SVG children inside a single .map() body. */}39 <g>40 {nodes().map(n => (41 <g key={n.id} data-node-id={n.id} onPointerDown={onNodeDown}>42 <circle cx={n.x} cy={n.y} r={28} fill={KIND_FILL[n.kind]}/>43 <text x={n.x} y={n.y}>{n.label}</text>44 <circle cx={n.x + 28} cy={n.y} r={5} onPointerDown={onHandleDown}/>45 </g>46 ))}47 </g>48 </svg>49 )50}#Features
SVG Namespace Attribute Bindings
Every other block in the gallery uses HTML elements only. This block is the first to bind signals to SVG attributes:cx andcy on<circle>,d on<path>,x /y on<text>, andviewBox on the root<svg>. The compiler must wire reactive bindings using the SVG namespace path so attributes update on the correct element.
Reactive `d` Path Rebuild
Each edge'sd attribute is recomputed from both endpoints' currentx /y. Dragging a node triggers d rebuilds for every edge connected to it, exercising path-string reactivity inside a mapArrayloop body.
Reactive viewBox
Zoom buttons rewrite the SVG viewBoxstring on every change, exercising attribute updates on the root SVG element.
Nested SVG Loops
The nodes loop renders a <g>wrapper containing a body<circle>, a<text> label, and a connect <circle>handle — three SVG children sharing the same loop scope. Each one carries reactive attribute bindings.
Drag-to-Connect with Preview Path
Pulling from a node's handle creates a temporary preview <path>tracking the cursor. The preview lives in a conditional branch (rendered only while dragging), exercising conditional SVG mounts. Releasing on another node creates a real edge.
Auto-Layout Swap
Toggling auto-layout replaces every node'sx /y simultaneously via topological column placement. Every reactivecx /cy /d binding must update on the same microtask without stale frames.