@glyphjs/components 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +432 -71
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -3
- package/dist/index.d.ts +6 -3
- package/dist/index.js +433 -72
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -75,7 +75,7 @@ function Callout({ data }) {
|
|
|
75
75
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "note", "aria-label": CALLOUT_LABELS[type], style: containerStyle11, children: [
|
|
76
76
|
/* @__PURE__ */ jsxRuntime.jsx("span", { style: iconStyle, "aria-hidden": "true", children: CALLOUT_ICONS[type] }),
|
|
77
77
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: bodyStyle3, children: [
|
|
78
|
-
title && /* @__PURE__ */ jsxRuntime.jsx("div", { style: titleStyle2, children: title }),
|
|
78
|
+
title && /* @__PURE__ */ jsxRuntime.jsx("div", { style: titleStyle2, children: /* @__PURE__ */ jsxRuntime.jsx(runtime.RichText, { content: title }) }),
|
|
79
79
|
/* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsx(runtime.RichText, { content }) })
|
|
80
80
|
] })
|
|
81
81
|
] });
|
|
@@ -1455,6 +1455,326 @@ function computeForceLayout(nodes, edges) {
|
|
|
1455
1455
|
height: maxY + LAYOUT_PADDING
|
|
1456
1456
|
};
|
|
1457
1457
|
}
|
|
1458
|
+
function useZoomInteraction({
|
|
1459
|
+
svgRef,
|
|
1460
|
+
rootRef,
|
|
1461
|
+
interactionMode
|
|
1462
|
+
}) {
|
|
1463
|
+
const [isActive, setIsActive] = react.useState(interactionMode === "always");
|
|
1464
|
+
const [hasAttemptedScroll, setHasAttemptedScroll] = react.useState(false);
|
|
1465
|
+
const containerRef = react.useRef(null);
|
|
1466
|
+
react.useEffect(() => {
|
|
1467
|
+
setIsActive(interactionMode === "always");
|
|
1468
|
+
setHasAttemptedScroll(false);
|
|
1469
|
+
}, [interactionMode]);
|
|
1470
|
+
const filterFunction = react.useCallback(
|
|
1471
|
+
(event) => {
|
|
1472
|
+
if (interactionMode === "always") {
|
|
1473
|
+
return true;
|
|
1474
|
+
}
|
|
1475
|
+
if (interactionMode === "modifier-key") {
|
|
1476
|
+
if (event.type === "mousedown") return true;
|
|
1477
|
+
if (event.type === "wheel") {
|
|
1478
|
+
const wheelEvent = event;
|
|
1479
|
+
const hasModifier = wheelEvent.altKey;
|
|
1480
|
+
if (!hasModifier && !hasAttemptedScroll) {
|
|
1481
|
+
setHasAttemptedScroll(true);
|
|
1482
|
+
setTimeout(() => setHasAttemptedScroll(false), 3e3);
|
|
1483
|
+
}
|
|
1484
|
+
return hasModifier;
|
|
1485
|
+
}
|
|
1486
|
+
return true;
|
|
1487
|
+
}
|
|
1488
|
+
if (interactionMode === "click-to-activate") {
|
|
1489
|
+
return isActive;
|
|
1490
|
+
}
|
|
1491
|
+
return true;
|
|
1492
|
+
},
|
|
1493
|
+
[interactionMode, isActive, hasAttemptedScroll]
|
|
1494
|
+
);
|
|
1495
|
+
react.useEffect(() => {
|
|
1496
|
+
if (interactionMode !== "modifier-key" || !svgRef.current) return;
|
|
1497
|
+
const svg = svgRef.current;
|
|
1498
|
+
const container = svg.parentElement;
|
|
1499
|
+
if (!container) return;
|
|
1500
|
+
const handleWheel = (event) => {
|
|
1501
|
+
const target = event.target;
|
|
1502
|
+
if (event.altKey && svg.contains(target)) {
|
|
1503
|
+
event.preventDefault();
|
|
1504
|
+
event.stopPropagation();
|
|
1505
|
+
}
|
|
1506
|
+
};
|
|
1507
|
+
container.addEventListener("wheel", handleWheel, { passive: false, capture: true });
|
|
1508
|
+
return () => {
|
|
1509
|
+
container.removeEventListener("wheel", handleWheel, { capture: true });
|
|
1510
|
+
};
|
|
1511
|
+
}, [interactionMode, svgRef]);
|
|
1512
|
+
const zoomBehavior = react.useMemo(() => {
|
|
1513
|
+
const zoom3 = d32__namespace.zoom().scaleExtent([0.1, 4]);
|
|
1514
|
+
if (typeof zoom3.filter === "function") {
|
|
1515
|
+
zoom3.filter(filterFunction);
|
|
1516
|
+
}
|
|
1517
|
+
zoom3.on("zoom", (event) => {
|
|
1518
|
+
if (rootRef.current) {
|
|
1519
|
+
d32__namespace.select(rootRef.current).attr("transform", event.transform.toString());
|
|
1520
|
+
}
|
|
1521
|
+
});
|
|
1522
|
+
return zoom3;
|
|
1523
|
+
}, [filterFunction, rootRef]);
|
|
1524
|
+
const handleActivate = react.useCallback(() => {
|
|
1525
|
+
if (interactionMode === "click-to-activate") {
|
|
1526
|
+
setIsActive(true);
|
|
1527
|
+
}
|
|
1528
|
+
}, [interactionMode]);
|
|
1529
|
+
react.useEffect(() => {
|
|
1530
|
+
if (interactionMode !== "click-to-activate" || !isActive) return;
|
|
1531
|
+
const handleKeyDown = (e) => {
|
|
1532
|
+
if (e.key === "Escape") {
|
|
1533
|
+
setIsActive(false);
|
|
1534
|
+
}
|
|
1535
|
+
};
|
|
1536
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
1537
|
+
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
1538
|
+
}, [interactionMode, isActive]);
|
|
1539
|
+
react.useEffect(() => {
|
|
1540
|
+
if (interactionMode !== "click-to-activate" || !isActive) return;
|
|
1541
|
+
const handleClickOutside = (e) => {
|
|
1542
|
+
const container = svgRef.current?.parentElement;
|
|
1543
|
+
if (container && !container.contains(e.target)) {
|
|
1544
|
+
setIsActive(false);
|
|
1545
|
+
}
|
|
1546
|
+
};
|
|
1547
|
+
document.addEventListener("click", handleClickOutside, true);
|
|
1548
|
+
return () => document.removeEventListener("click", handleClickOutside, true);
|
|
1549
|
+
}, [interactionMode, isActive, svgRef]);
|
|
1550
|
+
react.useEffect(() => {
|
|
1551
|
+
if (svgRef.current) {
|
|
1552
|
+
containerRef.current = svgRef.current.parentElement;
|
|
1553
|
+
}
|
|
1554
|
+
}, [svgRef]);
|
|
1555
|
+
const overlayProps = react.useMemo(() => {
|
|
1556
|
+
if (interactionMode === "always") return null;
|
|
1557
|
+
if (interactionMode === "modifier-key" && !hasAttemptedScroll) return null;
|
|
1558
|
+
if (interactionMode === "click-to-activate" && isActive) return null;
|
|
1559
|
+
return {
|
|
1560
|
+
mode: interactionMode,
|
|
1561
|
+
isActive,
|
|
1562
|
+
onActivate: handleActivate,
|
|
1563
|
+
width: "100%",
|
|
1564
|
+
height: "100%"
|
|
1565
|
+
};
|
|
1566
|
+
}, [interactionMode, isActive, hasAttemptedScroll, handleActivate]);
|
|
1567
|
+
const zoomIn = react.useCallback(() => {
|
|
1568
|
+
if (!svgRef.current) return;
|
|
1569
|
+
d32__namespace.select(svgRef.current).transition().duration(300).call(zoomBehavior.scaleBy, 1.3);
|
|
1570
|
+
}, [svgRef, zoomBehavior]);
|
|
1571
|
+
const zoomOut = react.useCallback(() => {
|
|
1572
|
+
if (!svgRef.current) return;
|
|
1573
|
+
d32__namespace.select(svgRef.current).transition().duration(300).call(zoomBehavior.scaleBy, 1 / 1.3);
|
|
1574
|
+
}, [svgRef, zoomBehavior]);
|
|
1575
|
+
const resetZoom = react.useCallback(() => {
|
|
1576
|
+
if (!svgRef.current) return;
|
|
1577
|
+
d32__namespace.select(svgRef.current).transition().duration(300).call(zoomBehavior.transform, d32__namespace.zoomIdentity);
|
|
1578
|
+
}, [svgRef, zoomBehavior]);
|
|
1579
|
+
return {
|
|
1580
|
+
isActive,
|
|
1581
|
+
overlayProps,
|
|
1582
|
+
zoomBehavior,
|
|
1583
|
+
zoomIn,
|
|
1584
|
+
zoomOut,
|
|
1585
|
+
resetZoom
|
|
1586
|
+
};
|
|
1587
|
+
}
|
|
1588
|
+
function InteractionOverlay({
|
|
1589
|
+
mode,
|
|
1590
|
+
isActive,
|
|
1591
|
+
onActivate,
|
|
1592
|
+
width,
|
|
1593
|
+
height
|
|
1594
|
+
}) {
|
|
1595
|
+
if (mode === "modifier-key") {
|
|
1596
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1597
|
+
"div",
|
|
1598
|
+
{
|
|
1599
|
+
className: "glyph-interaction-overlay",
|
|
1600
|
+
style: {
|
|
1601
|
+
...OVERLAY_BASE_STYLE,
|
|
1602
|
+
width,
|
|
1603
|
+
height,
|
|
1604
|
+
pointerEvents: "none"
|
|
1605
|
+
},
|
|
1606
|
+
"aria-hidden": "true",
|
|
1607
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: TOOLTIP_STYLE, children: /* @__PURE__ */ jsxRuntime.jsx("span", { style: TOOLTIP_TEXT_STYLE, children: "Alt + scroll to zoom" }) })
|
|
1608
|
+
}
|
|
1609
|
+
);
|
|
1610
|
+
}
|
|
1611
|
+
if (mode === "click-to-activate" && !isActive) {
|
|
1612
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1613
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1614
|
+
"div",
|
|
1615
|
+
{
|
|
1616
|
+
className: "glyph-interaction-overlay",
|
|
1617
|
+
style: {
|
|
1618
|
+
...OVERLAY_BASE_STYLE,
|
|
1619
|
+
...ACTIVATION_OVERLAY_STYLE,
|
|
1620
|
+
width,
|
|
1621
|
+
height
|
|
1622
|
+
},
|
|
1623
|
+
onClick: onActivate,
|
|
1624
|
+
role: "button",
|
|
1625
|
+
tabIndex: 0,
|
|
1626
|
+
"aria-label": "Click to activate graph interaction",
|
|
1627
|
+
onKeyDown: (e) => {
|
|
1628
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
1629
|
+
e.preventDefault();
|
|
1630
|
+
onActivate();
|
|
1631
|
+
}
|
|
1632
|
+
},
|
|
1633
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: ACTIVATION_TEXT_STYLE, children: "Click to interact" })
|
|
1634
|
+
}
|
|
1635
|
+
),
|
|
1636
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: SR_ONLY_STYLE, role: "status", "aria-live": "polite", "aria-atomic": "true", children: "Graph interaction inactive. Click to activate." })
|
|
1637
|
+
] });
|
|
1638
|
+
}
|
|
1639
|
+
if (mode === "click-to-activate" && isActive) {
|
|
1640
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1641
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1642
|
+
"div",
|
|
1643
|
+
{
|
|
1644
|
+
style: {
|
|
1645
|
+
...OVERLAY_BASE_STYLE,
|
|
1646
|
+
width,
|
|
1647
|
+
height,
|
|
1648
|
+
pointerEvents: "none",
|
|
1649
|
+
border: "2px solid var(--glyph-interaction-active-border, #0a9d7c)",
|
|
1650
|
+
borderRadius: "4px"
|
|
1651
|
+
},
|
|
1652
|
+
"aria-hidden": "true"
|
|
1653
|
+
}
|
|
1654
|
+
),
|
|
1655
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: SR_ONLY_STYLE, role: "status", "aria-live": "polite", "aria-atomic": "true", children: "Graph interaction active. Press Escape to deactivate." })
|
|
1656
|
+
] });
|
|
1657
|
+
}
|
|
1658
|
+
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, {});
|
|
1659
|
+
}
|
|
1660
|
+
var OVERLAY_BASE_STYLE = {
|
|
1661
|
+
position: "absolute",
|
|
1662
|
+
top: 0,
|
|
1663
|
+
left: 0,
|
|
1664
|
+
display: "flex",
|
|
1665
|
+
alignItems: "center",
|
|
1666
|
+
justifyContent: "center",
|
|
1667
|
+
zIndex: 10
|
|
1668
|
+
};
|
|
1669
|
+
var TOOLTIP_STYLE = {
|
|
1670
|
+
position: "absolute",
|
|
1671
|
+
bottom: "12px",
|
|
1672
|
+
right: "12px",
|
|
1673
|
+
padding: "6px 10px",
|
|
1674
|
+
backgroundColor: "var(--glyph-interaction-tooltip-bg, rgba(26, 32, 53, 0.9))",
|
|
1675
|
+
color: "var(--glyph-interaction-tooltip-text, #f4f6fa)",
|
|
1676
|
+
borderRadius: "4px",
|
|
1677
|
+
fontSize: "12px",
|
|
1678
|
+
fontFamily: "Inter, system-ui, sans-serif",
|
|
1679
|
+
fontWeight: 500,
|
|
1680
|
+
boxShadow: "0 2px 8px rgba(0, 0, 0, 0.15)",
|
|
1681
|
+
pointerEvents: "none"
|
|
1682
|
+
};
|
|
1683
|
+
var TOOLTIP_TEXT_STYLE = {
|
|
1684
|
+
display: "flex",
|
|
1685
|
+
alignItems: "center",
|
|
1686
|
+
gap: "4px"
|
|
1687
|
+
};
|
|
1688
|
+
var ACTIVATION_OVERLAY_STYLE = {
|
|
1689
|
+
backgroundColor: "var(--glyph-interaction-overlay-bg, rgba(244, 246, 250, 0.8))",
|
|
1690
|
+
cursor: "pointer",
|
|
1691
|
+
transition: "background-color 0.2s ease"
|
|
1692
|
+
};
|
|
1693
|
+
var ACTIVATION_TEXT_STYLE = {
|
|
1694
|
+
padding: "12px 20px",
|
|
1695
|
+
backgroundColor: "var(--glyph-interaction-tooltip-bg, rgba(26, 32, 53, 0.9))",
|
|
1696
|
+
color: "var(--glyph-interaction-tooltip-text, #f4f6fa)",
|
|
1697
|
+
borderRadius: "6px",
|
|
1698
|
+
fontSize: "14px",
|
|
1699
|
+
fontFamily: "Inter, system-ui, sans-serif",
|
|
1700
|
+
fontWeight: 500,
|
|
1701
|
+
boxShadow: "0 4px 12px rgba(0, 0, 0, 0.2)"
|
|
1702
|
+
};
|
|
1703
|
+
var SR_ONLY_STYLE = {
|
|
1704
|
+
position: "absolute",
|
|
1705
|
+
width: "1px",
|
|
1706
|
+
height: "1px",
|
|
1707
|
+
padding: 0,
|
|
1708
|
+
margin: "-1px",
|
|
1709
|
+
overflow: "hidden",
|
|
1710
|
+
clip: "rect(0, 0, 0, 0)",
|
|
1711
|
+
whiteSpace: "nowrap",
|
|
1712
|
+
border: 0
|
|
1713
|
+
};
|
|
1714
|
+
function ZoomControls({ onZoomIn, onZoomOut, onReset }) {
|
|
1715
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: CONTROLS_CONTAINER_STYLE, children: [
|
|
1716
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1717
|
+
"button",
|
|
1718
|
+
{
|
|
1719
|
+
onClick: onZoomIn,
|
|
1720
|
+
style: BUTTON_STYLE,
|
|
1721
|
+
"aria-label": "Zoom in",
|
|
1722
|
+
title: "Zoom in",
|
|
1723
|
+
type: "button",
|
|
1724
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", children: [
|
|
1725
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "8", y1: "4", x2: "8", y2: "12", strokeWidth: "2" }),
|
|
1726
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "4", y1: "8", x2: "12", y2: "8", strokeWidth: "2" })
|
|
1727
|
+
] })
|
|
1728
|
+
}
|
|
1729
|
+
),
|
|
1730
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1731
|
+
"button",
|
|
1732
|
+
{
|
|
1733
|
+
onClick: onZoomOut,
|
|
1734
|
+
style: BUTTON_STYLE,
|
|
1735
|
+
"aria-label": "Zoom out",
|
|
1736
|
+
title: "Zoom out",
|
|
1737
|
+
type: "button",
|
|
1738
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "4", y1: "8", x2: "12", y2: "8", strokeWidth: "2" }) })
|
|
1739
|
+
}
|
|
1740
|
+
),
|
|
1741
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1742
|
+
"button",
|
|
1743
|
+
{
|
|
1744
|
+
onClick: onReset,
|
|
1745
|
+
style: BUTTON_STYLE,
|
|
1746
|
+
"aria-label": "Reset zoom",
|
|
1747
|
+
title: "Reset zoom",
|
|
1748
|
+
type: "button",
|
|
1749
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "3", y: "3", width: "10", height: "10", strokeWidth: "2", rx: "1" }) })
|
|
1750
|
+
}
|
|
1751
|
+
)
|
|
1752
|
+
] });
|
|
1753
|
+
}
|
|
1754
|
+
var CONTROLS_CONTAINER_STYLE = {
|
|
1755
|
+
position: "absolute",
|
|
1756
|
+
top: "12px",
|
|
1757
|
+
right: "12px",
|
|
1758
|
+
display: "flex",
|
|
1759
|
+
flexDirection: "column",
|
|
1760
|
+
gap: "4px",
|
|
1761
|
+
zIndex: 10
|
|
1762
|
+
};
|
|
1763
|
+
var BUTTON_STYLE = {
|
|
1764
|
+
width: "32px",
|
|
1765
|
+
height: "32px",
|
|
1766
|
+
padding: "0",
|
|
1767
|
+
display: "flex",
|
|
1768
|
+
alignItems: "center",
|
|
1769
|
+
justifyContent: "center",
|
|
1770
|
+
backgroundColor: "var(--glyph-surface-raised, #f4f6fa)",
|
|
1771
|
+
border: "1px solid var(--glyph-border, #d0d8e4)",
|
|
1772
|
+
borderRadius: "4px",
|
|
1773
|
+
color: "var(--glyph-text, #1a2035)",
|
|
1774
|
+
cursor: "pointer",
|
|
1775
|
+
transition: "all 0.2s ease",
|
|
1776
|
+
boxShadow: "0 2px 4px rgba(0, 0, 0, 0.1)"
|
|
1777
|
+
};
|
|
1458
1778
|
var GROUP_PALETTE = [
|
|
1459
1779
|
"#00d4aa",
|
|
1460
1780
|
// cyan-green
|
|
@@ -1501,7 +1821,7 @@ function getThemeVar(container, varName, fallback) {
|
|
|
1501
1821
|
return getComputedStyle(container).getPropertyValue(varName).trim() || fallback;
|
|
1502
1822
|
}
|
|
1503
1823
|
var ARROW_MARKER_ID = "glyph-graph-arrowhead";
|
|
1504
|
-
function renderGraph(svgElement, layout, groupIndex, outgoingRefs, onNavigate, onNodeClick) {
|
|
1824
|
+
function renderGraph(svgElement, layout, groupIndex, outgoingRefs, onNavigate, zoomBehavior, onNodeClick) {
|
|
1505
1825
|
const svg = d32__namespace.select(svgElement);
|
|
1506
1826
|
svg.selectAll("*").remove();
|
|
1507
1827
|
const width = Math.max(layout.width, 200);
|
|
@@ -1514,9 +1834,6 @@ function renderGraph(svgElement, layout, groupIndex, outgoingRefs, onNavigate, o
|
|
|
1514
1834
|
const nodeStrokeWidth = getThemeVar(container, "--glyph-node-stroke-width", "1.5");
|
|
1515
1835
|
const nodeFillOpacity = getThemeVar(container, "--glyph-node-fill-opacity", "0.85");
|
|
1516
1836
|
const root = svg.append("g").attr("class", "glyph-graph-root");
|
|
1517
|
-
const zoomBehavior = d32__namespace.zoom().scaleExtent([0.1, 4]).on("zoom", (event) => {
|
|
1518
|
-
root.attr("transform", event.transform.toString());
|
|
1519
|
-
});
|
|
1520
1837
|
svg.call(zoomBehavior);
|
|
1521
1838
|
const navigableNodes = /* @__PURE__ */ new Set();
|
|
1522
1839
|
const refByAnchor = /* @__PURE__ */ new Map();
|
|
@@ -1573,6 +1890,7 @@ function Graph({
|
|
|
1573
1890
|
container
|
|
1574
1891
|
}) {
|
|
1575
1892
|
const svgRef = react.useRef(null);
|
|
1893
|
+
const rootRef = react.useRef(null);
|
|
1576
1894
|
const groupIndex = react.useRef(/* @__PURE__ */ new Map());
|
|
1577
1895
|
const layoutResult = react.useMemo(() => {
|
|
1578
1896
|
const direction = resolveLayout(data);
|
|
@@ -1581,6 +1899,12 @@ function Graph({
|
|
|
1581
1899
|
}
|
|
1582
1900
|
return computeDagreLayout(data.nodes, data.edges, direction);
|
|
1583
1901
|
}, [data]);
|
|
1902
|
+
const { overlayProps, zoomBehavior, zoomIn, zoomOut, resetZoom } = useZoomInteraction({
|
|
1903
|
+
svgRef,
|
|
1904
|
+
rootRef,
|
|
1905
|
+
interactionMode: data.interactionMode ?? "modifier-key",
|
|
1906
|
+
blockId: block.id
|
|
1907
|
+
});
|
|
1584
1908
|
const handleNodeClick = react.useMemo(() => {
|
|
1585
1909
|
if (!onInteraction) return void 0;
|
|
1586
1910
|
return (nodeId, nodeLabel) => {
|
|
@@ -1601,27 +1925,36 @@ function Graph({
|
|
|
1601
1925
|
groupIndex.current,
|
|
1602
1926
|
outgoingRefs,
|
|
1603
1927
|
onNavigate,
|
|
1928
|
+
zoomBehavior,
|
|
1604
1929
|
handleNodeClick
|
|
1605
1930
|
);
|
|
1606
|
-
|
|
1931
|
+
const rootElement = svgRef.current.querySelector(".glyph-graph-root");
|
|
1932
|
+
if (rootElement) {
|
|
1933
|
+
rootRef.current = rootElement;
|
|
1934
|
+
}
|
|
1935
|
+
}, [layoutResult, outgoingRefs, onNavigate, zoomBehavior, handleNodeClick]);
|
|
1607
1936
|
const ariaLabel = `${data.type} graph with ${data.nodes.length} nodes and ${data.edges.length} edges`;
|
|
1608
1937
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "glyph-graph-container", children: [
|
|
1609
|
-
/* @__PURE__ */ jsxRuntime.
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1938
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative" }, children: [
|
|
1939
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1940
|
+
"svg",
|
|
1941
|
+
{
|
|
1942
|
+
ref: svgRef,
|
|
1943
|
+
role: "img",
|
|
1944
|
+
"aria-label": ariaLabel,
|
|
1945
|
+
width: "100%",
|
|
1946
|
+
height: "100%",
|
|
1947
|
+
style: {
|
|
1948
|
+
minHeight: container.tier === "compact" ? 200 : 300,
|
|
1949
|
+
maxHeight: container.tier === "compact" ? 500 : 700,
|
|
1950
|
+
display: "block"
|
|
1951
|
+
}
|
|
1621
1952
|
}
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1953
|
+
),
|
|
1954
|
+
overlayProps && /* @__PURE__ */ jsxRuntime.jsx(InteractionOverlay, { ...overlayProps }),
|
|
1955
|
+
/* @__PURE__ */ jsxRuntime.jsx(ZoomControls, { onZoomIn: zoomIn, onZoomOut: zoomOut, onReset: resetZoom })
|
|
1956
|
+
] }),
|
|
1957
|
+
/* @__PURE__ */ jsxRuntime.jsxs("table", { className: "sr-only", "aria-label": "Graph data", style: SR_ONLY_STYLE2, children: [
|
|
1625
1958
|
/* @__PURE__ */ jsxRuntime.jsx("caption", { children: "Graph nodes and connections" }),
|
|
1626
1959
|
/* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
|
|
1627
1960
|
/* @__PURE__ */ jsxRuntime.jsx("th", { scope: "col", children: "Node" }),
|
|
@@ -1643,7 +1976,7 @@ function Graph({
|
|
|
1643
1976
|
] })
|
|
1644
1977
|
] });
|
|
1645
1978
|
}
|
|
1646
|
-
var
|
|
1979
|
+
var SR_ONLY_STYLE2 = {
|
|
1647
1980
|
position: "absolute",
|
|
1648
1981
|
width: "1px",
|
|
1649
1982
|
height: "1px",
|
|
@@ -1767,16 +2100,13 @@ function drawCrowsFoot(g, x, y, angle, symbol) {
|
|
|
1767
2100
|
g.append("line").attr("x1", tx - Math.cos(perpAngle) * halfLen).attr("y1", ty - Math.sin(perpAngle) * halfLen).attr("x2", tx + Math.cos(perpAngle) * halfLen).attr("y2", ty + Math.sin(perpAngle) * halfLen).attr("stroke", "var(--glyph-relation-line, #6b7a94)").attr("stroke-width", "var(--glyph-node-stroke-width, 1.5)");
|
|
1768
2101
|
}
|
|
1769
2102
|
}
|
|
1770
|
-
function renderRelation(svgElement, layout) {
|
|
2103
|
+
function renderRelation(svgElement, layout, zoomBehavior) {
|
|
1771
2104
|
const svg = d32__namespace.select(svgElement);
|
|
1772
2105
|
svg.selectAll("*").remove();
|
|
1773
2106
|
const width = Math.max(layout.width, 200);
|
|
1774
2107
|
const height = Math.max(layout.height, 200);
|
|
1775
2108
|
svg.attr("viewBox", `0 0 ${width} ${height}`);
|
|
1776
2109
|
const root = svg.append("g").attr("class", "glyph-relation-root");
|
|
1777
|
-
const zoomBehavior = d32__namespace.zoom().scaleExtent([0.1, 4]).on("zoom", (event) => {
|
|
1778
|
-
root.attr("transform", event.transform.toString());
|
|
1779
|
-
});
|
|
1780
2110
|
svg.call(zoomBehavior);
|
|
1781
2111
|
const entityMap = /* @__PURE__ */ new Map();
|
|
1782
2112
|
for (const entity of layout.entities) {
|
|
@@ -1848,29 +2178,44 @@ function renderRelation(svgElement, layout) {
|
|
|
1848
2178
|
}
|
|
1849
2179
|
}
|
|
1850
2180
|
}
|
|
1851
|
-
function Relation({ data }) {
|
|
2181
|
+
function Relation({ data, block }) {
|
|
1852
2182
|
const svgRef = react.useRef(null);
|
|
2183
|
+
const rootRef = react.useRef(null);
|
|
1853
2184
|
const layoutResult = react.useMemo(() => {
|
|
1854
2185
|
return computeRelationLayout(data);
|
|
1855
2186
|
}, [data]);
|
|
2187
|
+
const { overlayProps, zoomBehavior, zoomIn, zoomOut, resetZoom } = useZoomInteraction({
|
|
2188
|
+
svgRef,
|
|
2189
|
+
rootRef,
|
|
2190
|
+
interactionMode: data.interactionMode ?? "modifier-key",
|
|
2191
|
+
blockId: block.id
|
|
2192
|
+
});
|
|
1856
2193
|
react.useEffect(() => {
|
|
1857
2194
|
if (!svgRef.current) return;
|
|
1858
|
-
renderRelation(svgRef.current, layoutResult);
|
|
1859
|
-
|
|
2195
|
+
renderRelation(svgRef.current, layoutResult, zoomBehavior);
|
|
2196
|
+
const rootElement = svgRef.current.querySelector(".glyph-relation-root");
|
|
2197
|
+
if (rootElement) {
|
|
2198
|
+
rootRef.current = rootElement;
|
|
2199
|
+
}
|
|
2200
|
+
}, [layoutResult, zoomBehavior]);
|
|
1860
2201
|
const ariaLabel = `Entity-relationship diagram with ${data.entities.length} entities and ${data.relationships.length} relationships`;
|
|
1861
2202
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "glyph-relation-container", children: [
|
|
1862
|
-
/* @__PURE__ */ jsxRuntime.
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
2203
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative" }, children: [
|
|
2204
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2205
|
+
"svg",
|
|
2206
|
+
{
|
|
2207
|
+
ref: svgRef,
|
|
2208
|
+
role: "img",
|
|
2209
|
+
"aria-label": ariaLabel,
|
|
2210
|
+
width: "100%",
|
|
2211
|
+
height: "100%",
|
|
2212
|
+
style: { minHeight: 300, maxHeight: 700, display: "block" }
|
|
2213
|
+
}
|
|
2214
|
+
),
|
|
2215
|
+
overlayProps && /* @__PURE__ */ jsxRuntime.jsx(InteractionOverlay, { ...overlayProps }),
|
|
2216
|
+
/* @__PURE__ */ jsxRuntime.jsx(ZoomControls, { onZoomIn: zoomIn, onZoomOut: zoomOut, onReset: resetZoom })
|
|
2217
|
+
] }),
|
|
2218
|
+
/* @__PURE__ */ jsxRuntime.jsxs("table", { className: "sr-only", "aria-label": "Entity-relationship data", style: SR_ONLY_STYLE3, children: [
|
|
1874
2219
|
/* @__PURE__ */ jsxRuntime.jsx("caption", { children: "Entities and relationships" }),
|
|
1875
2220
|
/* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
|
|
1876
2221
|
/* @__PURE__ */ jsxRuntime.jsx("th", { scope: "col", children: "Entity" }),
|
|
@@ -1893,7 +2238,7 @@ function Relation({ data }) {
|
|
|
1893
2238
|
] })
|
|
1894
2239
|
] });
|
|
1895
2240
|
}
|
|
1896
|
-
var
|
|
2241
|
+
var SR_ONLY_STYLE3 = {
|
|
1897
2242
|
position: "absolute",
|
|
1898
2243
|
width: "1px",
|
|
1899
2244
|
height: "1px",
|
|
@@ -2529,7 +2874,7 @@ function renderNodeShape(nodeG, node, fillOpacity, strokeWidth) {
|
|
|
2529
2874
|
}
|
|
2530
2875
|
}
|
|
2531
2876
|
}
|
|
2532
|
-
function renderFlowchart(svgElement, layout) {
|
|
2877
|
+
function renderFlowchart(svgElement, layout, zoomBehavior) {
|
|
2533
2878
|
const svg = d32__namespace.select(svgElement);
|
|
2534
2879
|
svg.selectAll("*").remove();
|
|
2535
2880
|
const width = Math.max(layout.width, 200);
|
|
@@ -2541,9 +2886,6 @@ function renderFlowchart(svgElement, layout) {
|
|
|
2541
2886
|
const nodeStrokeWidth = getThemeVar2(container, "--glyph-node-stroke-width", "1.5");
|
|
2542
2887
|
const nodeFillOpacity = getThemeVar2(container, "--glyph-node-fill-opacity", "0.85");
|
|
2543
2888
|
const root = svg.append("g").attr("class", "glyph-flowchart-root");
|
|
2544
|
-
const zoomBehavior = d32__namespace.zoom().scaleExtent([0.1, 4]).on("zoom", (event) => {
|
|
2545
|
-
root.attr("transform", event.transform.toString());
|
|
2546
|
-
});
|
|
2547
2889
|
svg.call(zoomBehavior);
|
|
2548
2890
|
const lineGen = d32__namespace.line().x((d) => d.x).y((d) => d.y).curve(d32__namespace.curveBasis);
|
|
2549
2891
|
const edgeGroup = root.append("g").attr("class", "glyph-flowchart-edges");
|
|
@@ -2564,13 +2906,28 @@ function renderFlowchart(svgElement, layout) {
|
|
|
2564
2906
|
nodeG.append("text").attr("x", node.x).attr("y", node.y).attr("dy", "0.35em").attr("text-anchor", "middle").attr("font-size", "13px").attr("font-family", "Inter, system-ui, sans-serif").attr("fill", "var(--glyph-node-label-color, #fff)").attr("pointer-events", "none").text(node.label);
|
|
2565
2907
|
}
|
|
2566
2908
|
}
|
|
2567
|
-
function Flowchart({
|
|
2909
|
+
function Flowchart({
|
|
2910
|
+
data,
|
|
2911
|
+
block,
|
|
2912
|
+
container
|
|
2913
|
+
}) {
|
|
2568
2914
|
const svgRef = react.useRef(null);
|
|
2915
|
+
const rootRef = react.useRef(null);
|
|
2569
2916
|
const layoutResult = react.useMemo(() => computeLayout(data.nodes, data.edges, data.direction), [data]);
|
|
2917
|
+
const { overlayProps, zoomBehavior, zoomIn, zoomOut, resetZoom } = useZoomInteraction({
|
|
2918
|
+
svgRef,
|
|
2919
|
+
rootRef,
|
|
2920
|
+
interactionMode: data.interactionMode ?? "modifier-key",
|
|
2921
|
+
blockId: block.id
|
|
2922
|
+
});
|
|
2570
2923
|
react.useEffect(() => {
|
|
2571
2924
|
if (!svgRef.current) return;
|
|
2572
|
-
renderFlowchart(svgRef.current, layoutResult);
|
|
2573
|
-
|
|
2925
|
+
renderFlowchart(svgRef.current, layoutResult, zoomBehavior);
|
|
2926
|
+
const rootElement = svgRef.current.querySelector(".glyph-flowchart-root");
|
|
2927
|
+
if (rootElement) {
|
|
2928
|
+
rootRef.current = rootElement;
|
|
2929
|
+
}
|
|
2930
|
+
}, [layoutResult, zoomBehavior]);
|
|
2574
2931
|
const nodeCount = data.nodes.length;
|
|
2575
2932
|
const edgeCount = data.edges.length;
|
|
2576
2933
|
const ariaLabel = data.title ? `${data.title}: flowchart with ${nodeCount} nodes and ${edgeCount} edges` : `Flowchart with ${nodeCount} nodes and ${edgeCount} edges`;
|
|
@@ -2588,22 +2945,26 @@ function Flowchart({ data, container }) {
|
|
|
2588
2945
|
children: data.title
|
|
2589
2946
|
}
|
|
2590
2947
|
),
|
|
2591
|
-
/* @__PURE__ */ jsxRuntime.
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2948
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative" }, children: [
|
|
2949
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2950
|
+
"svg",
|
|
2951
|
+
{
|
|
2952
|
+
ref: svgRef,
|
|
2953
|
+
role: "img",
|
|
2954
|
+
"aria-label": ariaLabel,
|
|
2955
|
+
width: "100%",
|
|
2956
|
+
height: "100%",
|
|
2957
|
+
style: {
|
|
2958
|
+
minHeight: container.tier === "compact" ? 200 : 300,
|
|
2959
|
+
maxHeight: container.tier === "compact" ? 500 : 700,
|
|
2960
|
+
display: "block"
|
|
2961
|
+
}
|
|
2603
2962
|
}
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2963
|
+
),
|
|
2964
|
+
overlayProps && /* @__PURE__ */ jsxRuntime.jsx(InteractionOverlay, { ...overlayProps }),
|
|
2965
|
+
/* @__PURE__ */ jsxRuntime.jsx(ZoomControls, { onZoomIn: zoomIn, onZoomOut: zoomOut, onReset: resetZoom })
|
|
2966
|
+
] }),
|
|
2967
|
+
/* @__PURE__ */ jsxRuntime.jsxs("table", { className: "sr-only", "aria-label": "Flowchart data", style: SR_ONLY_STYLE4, children: [
|
|
2607
2968
|
/* @__PURE__ */ jsxRuntime.jsx("caption", { children: "Flowchart nodes and connections" }),
|
|
2608
2969
|
/* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
|
|
2609
2970
|
/* @__PURE__ */ jsxRuntime.jsx("th", { scope: "col", children: "Node" }),
|
|
@@ -2625,7 +2986,7 @@ function Flowchart({ data, container }) {
|
|
|
2625
2986
|
] })
|
|
2626
2987
|
] });
|
|
2627
2988
|
}
|
|
2628
|
-
var
|
|
2989
|
+
var SR_ONLY_STYLE4 = {
|
|
2629
2990
|
position: "absolute",
|
|
2630
2991
|
width: "1px",
|
|
2631
2992
|
height: "1px",
|
|
@@ -3247,7 +3608,7 @@ function Sequence({ data, container }) {
|
|
|
3247
3608
|
]
|
|
3248
3609
|
}
|
|
3249
3610
|
),
|
|
3250
|
-
/* @__PURE__ */ jsxRuntime.jsxs("table", { className: "sr-only", "aria-label": "Sequence data", style:
|
|
3611
|
+
/* @__PURE__ */ jsxRuntime.jsxs("table", { className: "sr-only", "aria-label": "Sequence data", style: SR_ONLY_STYLE5, children: [
|
|
3251
3612
|
/* @__PURE__ */ jsxRuntime.jsx("caption", { children: "Sequence messages in order" }),
|
|
3252
3613
|
/* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
|
|
3253
3614
|
/* @__PURE__ */ jsxRuntime.jsx("th", { scope: "col", children: "#" }),
|
|
@@ -3270,7 +3631,7 @@ function Sequence({ data, container }) {
|
|
|
3270
3631
|
] })
|
|
3271
3632
|
] });
|
|
3272
3633
|
}
|
|
3273
|
-
var
|
|
3634
|
+
var SR_ONLY_STYLE5 = {
|
|
3274
3635
|
position: "absolute",
|
|
3275
3636
|
width: "1px",
|
|
3276
3637
|
height: "1px",
|
|
@@ -3676,7 +4037,7 @@ function Architecture({
|
|
|
3676
4037
|
}
|
|
3677
4038
|
}
|
|
3678
4039
|
),
|
|
3679
|
-
/* @__PURE__ */ jsxRuntime.jsxs("table", { className: "sr-only", "aria-label": "Architecture data", style:
|
|
4040
|
+
/* @__PURE__ */ jsxRuntime.jsxs("table", { className: "sr-only", "aria-label": "Architecture data", style: SR_ONLY_STYLE6, children: [
|
|
3680
4041
|
/* @__PURE__ */ jsxRuntime.jsx("caption", { children: "Architecture nodes and connections" }),
|
|
3681
4042
|
/* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
|
|
3682
4043
|
/* @__PURE__ */ jsxRuntime.jsx("th", { scope: "col", children: "Node" }),
|
|
@@ -3720,7 +4081,7 @@ function countLeafNodes(children) {
|
|
|
3720
4081
|
}
|
|
3721
4082
|
return count;
|
|
3722
4083
|
}
|
|
3723
|
-
var
|
|
4084
|
+
var SR_ONLY_STYLE6 = {
|
|
3724
4085
|
position: "absolute",
|
|
3725
4086
|
width: "1px",
|
|
3726
4087
|
height: "1px",
|
|
@@ -3908,7 +4269,7 @@ function layoutTree(data) {
|
|
|
3908
4269
|
};
|
|
3909
4270
|
}
|
|
3910
4271
|
function renderAccessibleList(root, children) {
|
|
3911
|
-
return /* @__PURE__ */ jsxRuntime.jsx("ul", { style:
|
|
4272
|
+
return /* @__PURE__ */ jsxRuntime.jsx("ul", { style: SR_ONLY_STYLE7, role: "list", "aria-label": "Mind map structure", children: /* @__PURE__ */ jsxRuntime.jsxs("li", { children: [
|
|
3912
4273
|
root,
|
|
3913
4274
|
children.length > 0 && renderAccessibleChildren(children)
|
|
3914
4275
|
] }) });
|
|
@@ -4035,7 +4396,7 @@ function MindMap({ data, container }) {
|
|
|
4035
4396
|
renderAccessibleList(data.root, data.children)
|
|
4036
4397
|
] });
|
|
4037
4398
|
}
|
|
4038
|
-
var
|
|
4399
|
+
var SR_ONLY_STYLE7 = {
|
|
4039
4400
|
position: "absolute",
|
|
4040
4401
|
width: "1px",
|
|
4041
4402
|
height: "1px",
|