"use client";
import * as React from 'react';
import { clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';
import { jsxs, jsx } from 'react/jsx-runtime';

function cn(...inputs) {
  return twMerge(clsx(inputs));
}
function Flowchart({
  nodes,
  edges,
  direction = "vertical",
  width = 720,
  height = 480,
  nodeWidth = 140,
  nodeHeight = 48,
  onNodeClick,
  className,
  ...rest
}) {
  const { nodes: laidOutNodes, edges: laidOutEdges } = React.useMemo(
    () => layout(nodes, edges, direction, width, height, nodeWidth, nodeHeight),
    [nodes, edges, direction, width, height, nodeWidth, nodeHeight]
  );
  const desc = `Flowchart with ${nodes.length} node${nodes.length === 1 ? "" : "s"} and ${edges.length} edge${edges.length === 1 ? "" : "s"}, laid out ${direction}`;
  const arrowId = React.useId().replace(/:/g, "-");
  return /* @__PURE__ */ jsxs(
    "svg",
    {
      ...rest,
      "data-hex-flowchart": true,
      "data-direction": direction,
      role: "img",
      width,
      height,
      viewBox: `0 0 ${width} ${height}`,
      className: cn("block", className),
      children: [
        /* @__PURE__ */ jsx("title", { children: "Flowchart" }),
        /* @__PURE__ */ jsx("desc", { children: desc }),
        /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx(
          "marker",
          {
            id: `hex-flowchart-arrow-${arrowId}`,
            viewBox: "0 0 10 10",
            refX: "10",
            refY: "5",
            markerWidth: "6",
            markerHeight: "6",
            orient: "auto-start-reverse",
            children: /* @__PURE__ */ jsx("path", { d: "M 0 0 L 10 5 L 0 10 z", fill: "hsl(var(--muted-foreground))" })
          }
        ) }),
        /* @__PURE__ */ jsx("g", { "data-hex-flowchart-edges": true, children: laidOutEdges.map((e, i) => /* @__PURE__ */ jsxs("g", { "data-hex-flowchart-edge": true, children: [
          /* @__PURE__ */ jsx(
            "path",
            {
              d: edgePath(e.from, e.to, direction),
              fill: "none",
              stroke: "hsl(var(--muted-foreground))",
              strokeOpacity: 0.7,
              strokeWidth: 1.25,
              markerEnd: `url(#hex-flowchart-arrow-${arrowId})`
            }
          ),
          e.edge.label ? /* @__PURE__ */ jsx(
            "text",
            {
              x: (e.from.x + e.to.x) / 2,
              y: (e.from.y + e.to.y) / 2 - 4,
              textAnchor: "middle",
              fontSize: 10,
              fill: "hsl(var(--muted-foreground))",
              style: {
                paintOrder: "stroke"
              },
              stroke: "hsl(var(--background))",
              strokeWidth: 3,
              strokeLinejoin: "round",
              children: e.edge.label
            }
          ) : null
        ] }, `${e.edge.source}-${e.edge.target}-${i}`)) }),
        /* @__PURE__ */ jsx("g", { "data-hex-flowchart-nodes": true, children: laidOutNodes.map((n) => {
          const shape = n.node.shape ?? "rect";
          const interactive = Boolean(onNodeClick);
          const handleActivate = () => onNodeClick?.(n.node);
          const truncated = truncate(n.node.label, Math.floor((nodeWidth - 16) / 7));
          const isTruncated = truncated !== n.node.label;
          return /* @__PURE__ */ jsxs(
            "g",
            {
              "data-hex-flowchart-node": true,
              "data-shape": shape,
              "data-rank": n.rank,
              className: interactive ? "focus-visible:outline-none focus-visible:outline-2 focus-visible:outline-ring" : void 0,
              transform: `translate(${n.x - nodeWidth / 2},${n.y - nodeHeight / 2})`,
              role: interactive ? "button" : void 0,
              tabIndex: interactive ? 0 : void 0,
              "aria-label": interactive ? n.node.label : void 0,
              style: interactive ? { cursor: "pointer" } : void 0,
              onClick: interactive ? handleActivate : void 0,
              onKeyDown: interactive ? (e) => activateOnKey(e, handleActivate) : void 0,
              children: [
                isTruncated ? /* @__PURE__ */ jsx("title", { children: n.node.label }) : null,
                shape === "diamond" ? /* @__PURE__ */ jsx(
                  "polygon",
                  {
                    points: `${nodeWidth / 2},0 ${nodeWidth},${nodeHeight / 2} ${nodeWidth / 2},${nodeHeight} 0,${nodeHeight / 2}`,
                    fill: "hsl(var(--card))",
                    stroke: "hsl(var(--border))",
                    strokeWidth: 1
                  }
                ) : /* @__PURE__ */ jsx(
                  "rect",
                  {
                    width: nodeWidth,
                    height: nodeHeight,
                    rx: shape === "round" ? nodeHeight / 2 : 6,
                    ry: shape === "round" ? nodeHeight / 2 : 6,
                    fill: "hsl(var(--card))",
                    stroke: "hsl(var(--border))",
                    strokeWidth: 1
                  }
                ),
                /* @__PURE__ */ jsx(
                  "text",
                  {
                    x: nodeWidth / 2,
                    y: nodeHeight / 2,
                    dy: "0.35em",
                    textAnchor: "middle",
                    fontSize: 12,
                    fontWeight: 500,
                    fill: "hsl(var(--foreground))",
                    style: { pointerEvents: "none" },
                    children: truncated
                  }
                )
              ]
            },
            n.node.id
          );
        }) })
      ]
    }
  );
}
function layout(nodes, edges, direction, width, height, nodeWidth, nodeHeight) {
  if (nodes.length === 0) return { nodes: [], edges: [] };
  const byId = new Map(nodes.map((n) => [n.id, n]));
  const ranks = computeRanks(nodes, edges, byId);
  const maxRank = Math.max(...ranks.values(), 0);
  const rankGroups = /* @__PURE__ */ new Map();
  for (const n of nodes) {
    const r = ranks.get(n.id) ?? 0;
    const arr = rankGroups.get(r) ?? [];
    arr.push(n.id);
    rankGroups.set(r, arr);
  }
  const parentsOf = /* @__PURE__ */ new Map();
  for (const n of nodes) parentsOf.set(n.id, []);
  for (const e of edges) {
    const arr = parentsOf.get(e.target);
    if (arr && byId.has(e.source)) arr.push(e.source);
  }
  const orderInRank = /* @__PURE__ */ new Map();
  for (let r = 0; r <= maxRank; r++) {
    const group = rankGroups.get(r) ?? [];
    if (r > 0) {
      group.sort((a, b) => meanParentIndex(a, parentsOf, orderInRank) - meanParentIndex(b, parentsOf, orderInRank));
    }
    group.forEach((id, i) => orderInRank.set(id, i));
  }
  const positions = /* @__PURE__ */ new Map();
  const margin = 32;
  const usableMain = (direction === "vertical" ? height : width) - margin * 2;
  const usableCross = (direction === "vertical" ? width : height) - margin * 2;
  const rankCount = maxRank + 1;
  const mainStep = rankCount > 1 ? usableMain / (rankCount - 1) : 0;
  for (let r = 0; r <= maxRank; r++) {
    const group = rankGroups.get(r) ?? [];
    const crossStep = group.length > 0 ? usableCross / (group.length + 1) : 0;
    group.forEach((id, i) => {
      const cross = margin + crossStep * (i + 1);
      const main = margin + mainStep * r;
      if (direction === "vertical") {
        positions.set(id, { x: cross, y: main, rank: r });
      } else {
        positions.set(id, { x: main, y: cross, rank: r });
      }
    });
  }
  const laidOutNodes = nodes.map((n) => {
    const p = positions.get(n.id) ?? { x: 0, y: 0, rank: 0 };
    return { node: n, x: p.x, y: p.y, rank: p.rank };
  });
  const laidOutEdges = edges.map((edge) => {
    const sp = positions.get(edge.source);
    const tp = positions.get(edge.target);
    if (!sp || !tp) return null;
    const from = direction === "vertical" ? { x: sp.x, y: sp.y + nodeHeight / 2 } : { x: sp.x + nodeWidth / 2, y: sp.y };
    const to = direction === "vertical" ? { x: tp.x, y: tp.y - nodeHeight / 2 } : { x: tp.x - nodeWidth / 2, y: tp.y };
    return { edge, from, to };
  }).filter((e) => e !== null);
  return { nodes: laidOutNodes, edges: laidOutEdges };
}
function meanParentIndex(id, parentsOf, orderInRank) {
  const parents = parentsOf.get(id) ?? [];
  if (parents.length === 0) return Number.POSITIVE_INFINITY;
  let sum = 0;
  let count = 0;
  for (const p of parents) {
    const idx = orderInRank.get(p);
    if (idx != null) {
      sum += idx;
      count++;
    }
  }
  return count === 0 ? Number.POSITIVE_INFINITY : sum / count;
}
function computeRanks(nodes, edges, byId) {
  const ranks = /* @__PURE__ */ new Map();
  const incoming = /* @__PURE__ */ new Map();
  for (const n of nodes) incoming.set(n.id, []);
  for (const e of edges) {
    const arr = incoming.get(e.target);
    if (arr && byId.has(e.source)) arr.push(e.source);
  }
  const memo = /* @__PURE__ */ new Map();
  const visiting = /* @__PURE__ */ new Set();
  const rankOf = (id) => {
    const cached = memo.get(id);
    if (cached !== void 0) return cached;
    if (visiting.has(id)) return 0;
    visiting.add(id);
    try {
      const node = byId.get(id);
      if (node?.rank != null) {
        memo.set(id, node.rank);
        return node.rank;
      }
      const parents = incoming.get(id) ?? [];
      const r = parents.length === 0 ? 0 : 1 + Math.max(...parents.map((p) => rankOf(p)));
      memo.set(id, r);
      return r;
    } finally {
      visiting.delete(id);
    }
  };
  for (const n of nodes) ranks.set(n.id, rankOf(n.id));
  return ranks;
}
function edgePath(from, to, direction) {
  if (direction === "vertical") {
    const my = (from.y + to.y) / 2;
    return `M${from.x},${from.y} C${from.x},${my} ${to.x},${my} ${to.x},${to.y}`;
  }
  const mx = (from.x + to.x) / 2;
  return `M${from.x},${from.y} C${mx},${from.y} ${mx},${to.y} ${to.x},${to.y}`;
}
function truncate(s, max) {
  if (s.length <= max) return s;
  return `${s.slice(0, Math.max(1, max - 1))}\u2026`;
}
function activateOnKey(e, fn) {
  if (e.key === "Enter" || e.key === " ") {
    e.preventDefault();
    fn();
  }
}

export { Flowchart };
//# sourceMappingURL=flowchart.js.map
//# sourceMappingURL=flowchart.js.map