{"spec_id":"network-hierarchical","library":"muix","language":"javascript","code":"// anyplot.ai\n// network-hierarchical: Hierarchical Network Graph with Tree Layout\n// Library: muix 7.29.1 | JavaScript 22.23.2\n// Quality: 90/100 | Created: 2026-09-02\nimport * as React from \"react\";\nimport { ChartContainer } from \"@mui/x-charts/ChartContainer\";\nimport { useXScale, useYScale } from \"@mui/x-charts/hooks\";\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data: a 4-level company org chart (in-memory, deterministic) ----------\nconst DEPARTMENTS = [\n  {\n    name: \"Engineering\",\n    directors: [\n      { name: \"Platform Eng\", reports: [\"Backend Eng 1\", \"Backend Eng 2\", \"Infra Eng\"] },\n      { name: \"Product Eng\", reports: [\"Frontend Eng 1\", \"Frontend Eng 2\"] },\n    ],\n  },\n  {\n    name: \"Sales\",\n    directors: [\n      { name: \"Enterprise Sales\", reports: [\"Account Exec 1\", \"Account Exec 2\"] },\n      { name: \"SMB Sales\", reports: [\"Account Exec 3\", \"Account Exec 4\"] },\n    ],\n  },\n  {\n    name: \"Marketing\",\n    directors: [\n      { name: \"Brand\", reports: [\"Brand Manager\", \"Content Lead\"] },\n      { name: \"Growth\", reports: [\"Growth Analyst\", \"SEO Specialist\"] },\n    ],\n  },\n  {\n    name: \"Operations\",\n    directors: [\n      { name: \"Finance\", reports: [\"Controller\", \"FP&A Analyst\"] },\n      { name: \"People\", reports: [\"Recruiter\", \"HR Partner\"] },\n    ],\n  },\n];\n\nlet nextId = 0;\nconst nodesById = new Map();\n\nfunction makeNode(label, level, branch, parentId) {\n  const node = { id: nextId, label, level, branch, parentId, children: [] };\n  nextId += 1;\n  nodesById.set(node.id, node);\n  if (parentId !== null) nodesById.get(parentId).children.push(node.id);\n  return node;\n}\n\nconst ceo = makeNode(\"CEO\", 0, -1, null);\nDEPARTMENTS.forEach((dept, branch) => {\n  const vp = makeNode(`VP ${dept.name}`, 1, branch, ceo.id);\n  dept.directors.forEach((dir) => {\n    const director = makeNode(dir.name, 2, branch, vp.id);\n    dir.reports.forEach((reportName) => {\n      makeNode(reportName, 3, branch, director.id);\n    });\n  });\n});\n\n// --- Tidy tree layout: leaves get sequential x, parents average children ---\nlet nextLeafX = 0;\nfunction assignX(node) {\n  if (node.children.length === 0) {\n    node.x = nextLeafX;\n    nextLeafX += 1;\n    return node.x;\n  }\n  const childXs = node.children.map((childId) => assignX(nodesById.get(childId)));\n  node.x = childXs.reduce((sum, x) => sum + x, 0) / childXs.length;\n  return node.x;\n}\nassignX(ceo);\n\nconst MAX_LEVEL = 3;\nnodesById.forEach((node) => {\n  node.y = MAX_LEVEL - node.level; // root at top, leaves at bottom\n});\n\nconst allNodes = Array.from(nodesById.values());\nconst edges = allNodes.filter((n) => n.parentId !== null).map((n) => [n.parentId, n.id]);\n\nconst LEVEL_RADIUS = [22, 16, 12, 8];\nconst nodeRadius = (node) => LEVEL_RADIUS[node.level];\nconst nodeColor = (node) => (node.branch === -1 ? t.ink : t.palette[node.branch]);\n\n// Padded domain around the tidy-tree coordinates.\nconst xMax = nextLeafX - 1;\nconst X_PAD = 1.1;\nconst Y_PAD = 0.6;\nconst domain = {\n  xMin: -X_PAD,\n  xMax: xMax + X_PAD,\n  yMin: -Y_PAD,\n  yMax: MAX_LEVEL + Y_PAD,\n};\n\n// --- Custom SVG layers, positioned via the chart's own scales --------------\nfunction TreeEdges() {\n  const xScale = useXScale();\n  const yScale = useYScale();\n  return (\n    <g data-drawing-container>\n      {edges.map(([parentId, childId], i) => {\n        const parent = nodesById.get(parentId);\n        const child = nodesById.get(childId);\n        return (\n          <line\n            key={`edge-${i}`}\n            x1={xScale(parent.x)}\n            y1={yScale(parent.y)}\n            x2={xScale(child.x)}\n            y2={yScale(child.y)}\n            stroke={nodeColor(child)}\n            strokeOpacity={0.35}\n            strokeWidth={1.4}\n          />\n        );\n      })}\n    </g>\n  );\n}\n\nfunction TreeNodes() {\n  const xScale = useXScale();\n  const yScale = useYScale();\n  return (\n    <g data-drawing-container>\n      {allNodes.map((node) => (\n        <React.Fragment key={node.id}>\n          <circle\n            cx={xScale(node.x)}\n            cy={yScale(node.y)}\n            r={nodeRadius(node)}\n            fill={nodeColor(node)}\n            stroke={t.pageBg}\n            strokeWidth={1.5}\n          />\n          {node.level <= 2 && (\n            <text\n              x={xScale(node.x)}\n              y={yScale(node.y) - nodeRadius(node) - 8}\n              textAnchor=\"middle\"\n              fontSize={node.level === 0 ? 15 : 13}\n              fontWeight={node.level === 0 ? 600 : 400}\n              fill={t.ink}\n            >\n              {node.label}\n            </text>\n          )}\n        </React.Fragment>\n      ))}\n    </g>\n  );\n}\n\n// --- Title + legend chrome ---------------------------------------------------\nconst TITLE = \"network-hierarchical · javascript · muix · anyplot.ai\";\nconst TITLE_FONT_DEFAULT = 22;\nconst titleFontSize =\n  TITLE.length > 67 ? Math.round(TITLE_FONT_DEFAULT * (67 / TITLE.length)) : TITLE_FONT_DEFAULT;\nconst TITLE_H = 42;\nconst LEGEND_H = 34;\n\nfunction Legend() {\n  return (\n    <div style={{ height: LEGEND_H, display: \"flex\", alignItems: \"center\", gap: \"20px\", flexWrap: \"wrap\" }}>\n      {DEPARTMENTS.map((dept, i) => (\n        <div key={dept.name} style={{ display: \"flex\", alignItems: \"center\", gap: \"7px\" }}>\n          <span\n            style={{\n              width: \"12px\",\n              height: \"12px\",\n              borderRadius: \"50%\",\n              backgroundColor: t.palette[i],\n              display: \"inline-block\",\n            }}\n          />\n          <span style={{ fontSize: \"14px\", color: t.inkSoft }}>{dept.name}</span>\n        </div>\n      ))}\n    </div>\n  );\n}\n\n// --- Chart (default-exported component — the harness mounts it) ------------\nexport default function Chart() {\n  const { width, height } = window.ANYPLOT_SIZE;\n  const chartHeight = height - TITLE_H - LEGEND_H;\n\n  return (\n    <div style={{ width, height, display: \"flex\", flexDirection: \"column\" }}>\n      <div\n        style={{\n          height: `${TITLE_H}px`,\n          lineHeight: `${TITLE_H}px`,\n          fontSize: `${titleFontSize}px`,\n          fontWeight: 500,\n          color: t.ink,\n        }}\n      >\n        {TITLE}\n      </div>\n      <Legend />\n      <ChartContainer\n        width={width}\n        height={chartHeight}\n        series={[]}\n        margin={{ top: 8, bottom: 8, left: 8, right: 8 }}\n        xAxis={[{ id: \"x\", scaleType: \"linear\", min: domain.xMin, max: domain.xMax }]}\n        yAxis={[{ id: \"y\", scaleType: \"linear\", min: domain.yMin, max: domain.yMax }]}\n        skipAnimation\n      >\n        <TreeEdges />\n        <TreeNodes />\n      </ChartContainer>\n    </div>\n  );\n}\n"}