@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.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { accordionSchema, annotateSchema, architectureSchema, calloutSchema, cardSchema, chartSchema, codediffSchema, comparisonSchema, equationSchema, filetreeSchema, flowchartSchema, formSchema, graphSchema, infographicSchema, kanbanSchema, kpiSchema, matrixSchema, mindmapSchema, pollSchema, quizSchema, rankerSchema, ratingSchema, relationSchema, sequenceSchema, sliderSchema, stepsSchema, tableSchema, tabsSchema, timelineSchema } from '@glyphjs/schemas';
|
|
2
2
|
import { RichText } from '@glyphjs/runtime';
|
|
3
|
-
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
3
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
4
4
|
import { useRef, useState, useEffect, useCallback, useMemo } from 'react';
|
|
5
5
|
import * as d32 from 'd3';
|
|
6
6
|
import { scaleTime, scaleOrdinal } from 'd3';
|
|
@@ -50,7 +50,7 @@ function Callout({ data }) {
|
|
|
50
50
|
return /* @__PURE__ */ jsxs("div", { role: "note", "aria-label": CALLOUT_LABELS[type], style: containerStyle11, children: [
|
|
51
51
|
/* @__PURE__ */ jsx("span", { style: iconStyle, "aria-hidden": "true", children: CALLOUT_ICONS[type] }),
|
|
52
52
|
/* @__PURE__ */ jsxs("div", { style: bodyStyle3, children: [
|
|
53
|
-
title && /* @__PURE__ */ jsx("div", { style: titleStyle2, children: title }),
|
|
53
|
+
title && /* @__PURE__ */ jsx("div", { style: titleStyle2, children: /* @__PURE__ */ jsx(RichText, { content: title }) }),
|
|
54
54
|
/* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(RichText, { content }) })
|
|
55
55
|
] })
|
|
56
56
|
] });
|
|
@@ -1430,6 +1430,326 @@ function computeForceLayout(nodes, edges) {
|
|
|
1430
1430
|
height: maxY + LAYOUT_PADDING
|
|
1431
1431
|
};
|
|
1432
1432
|
}
|
|
1433
|
+
function useZoomInteraction({
|
|
1434
|
+
svgRef,
|
|
1435
|
+
rootRef,
|
|
1436
|
+
interactionMode
|
|
1437
|
+
}) {
|
|
1438
|
+
const [isActive, setIsActive] = useState(interactionMode === "always");
|
|
1439
|
+
const [hasAttemptedScroll, setHasAttemptedScroll] = useState(false);
|
|
1440
|
+
const containerRef = useRef(null);
|
|
1441
|
+
useEffect(() => {
|
|
1442
|
+
setIsActive(interactionMode === "always");
|
|
1443
|
+
setHasAttemptedScroll(false);
|
|
1444
|
+
}, [interactionMode]);
|
|
1445
|
+
const filterFunction = useCallback(
|
|
1446
|
+
(event) => {
|
|
1447
|
+
if (interactionMode === "always") {
|
|
1448
|
+
return true;
|
|
1449
|
+
}
|
|
1450
|
+
if (interactionMode === "modifier-key") {
|
|
1451
|
+
if (event.type === "mousedown") return true;
|
|
1452
|
+
if (event.type === "wheel") {
|
|
1453
|
+
const wheelEvent = event;
|
|
1454
|
+
const hasModifier = wheelEvent.altKey;
|
|
1455
|
+
if (!hasModifier && !hasAttemptedScroll) {
|
|
1456
|
+
setHasAttemptedScroll(true);
|
|
1457
|
+
setTimeout(() => setHasAttemptedScroll(false), 3e3);
|
|
1458
|
+
}
|
|
1459
|
+
return hasModifier;
|
|
1460
|
+
}
|
|
1461
|
+
return true;
|
|
1462
|
+
}
|
|
1463
|
+
if (interactionMode === "click-to-activate") {
|
|
1464
|
+
return isActive;
|
|
1465
|
+
}
|
|
1466
|
+
return true;
|
|
1467
|
+
},
|
|
1468
|
+
[interactionMode, isActive, hasAttemptedScroll]
|
|
1469
|
+
);
|
|
1470
|
+
useEffect(() => {
|
|
1471
|
+
if (interactionMode !== "modifier-key" || !svgRef.current) return;
|
|
1472
|
+
const svg = svgRef.current;
|
|
1473
|
+
const container = svg.parentElement;
|
|
1474
|
+
if (!container) return;
|
|
1475
|
+
const handleWheel = (event) => {
|
|
1476
|
+
const target = event.target;
|
|
1477
|
+
if (event.altKey && svg.contains(target)) {
|
|
1478
|
+
event.preventDefault();
|
|
1479
|
+
event.stopPropagation();
|
|
1480
|
+
}
|
|
1481
|
+
};
|
|
1482
|
+
container.addEventListener("wheel", handleWheel, { passive: false, capture: true });
|
|
1483
|
+
return () => {
|
|
1484
|
+
container.removeEventListener("wheel", handleWheel, { capture: true });
|
|
1485
|
+
};
|
|
1486
|
+
}, [interactionMode, svgRef]);
|
|
1487
|
+
const zoomBehavior = useMemo(() => {
|
|
1488
|
+
const zoom3 = d32.zoom().scaleExtent([0.1, 4]);
|
|
1489
|
+
if (typeof zoom3.filter === "function") {
|
|
1490
|
+
zoom3.filter(filterFunction);
|
|
1491
|
+
}
|
|
1492
|
+
zoom3.on("zoom", (event) => {
|
|
1493
|
+
if (rootRef.current) {
|
|
1494
|
+
d32.select(rootRef.current).attr("transform", event.transform.toString());
|
|
1495
|
+
}
|
|
1496
|
+
});
|
|
1497
|
+
return zoom3;
|
|
1498
|
+
}, [filterFunction, rootRef]);
|
|
1499
|
+
const handleActivate = useCallback(() => {
|
|
1500
|
+
if (interactionMode === "click-to-activate") {
|
|
1501
|
+
setIsActive(true);
|
|
1502
|
+
}
|
|
1503
|
+
}, [interactionMode]);
|
|
1504
|
+
useEffect(() => {
|
|
1505
|
+
if (interactionMode !== "click-to-activate" || !isActive) return;
|
|
1506
|
+
const handleKeyDown = (e) => {
|
|
1507
|
+
if (e.key === "Escape") {
|
|
1508
|
+
setIsActive(false);
|
|
1509
|
+
}
|
|
1510
|
+
};
|
|
1511
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
1512
|
+
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
1513
|
+
}, [interactionMode, isActive]);
|
|
1514
|
+
useEffect(() => {
|
|
1515
|
+
if (interactionMode !== "click-to-activate" || !isActive) return;
|
|
1516
|
+
const handleClickOutside = (e) => {
|
|
1517
|
+
const container = svgRef.current?.parentElement;
|
|
1518
|
+
if (container && !container.contains(e.target)) {
|
|
1519
|
+
setIsActive(false);
|
|
1520
|
+
}
|
|
1521
|
+
};
|
|
1522
|
+
document.addEventListener("click", handleClickOutside, true);
|
|
1523
|
+
return () => document.removeEventListener("click", handleClickOutside, true);
|
|
1524
|
+
}, [interactionMode, isActive, svgRef]);
|
|
1525
|
+
useEffect(() => {
|
|
1526
|
+
if (svgRef.current) {
|
|
1527
|
+
containerRef.current = svgRef.current.parentElement;
|
|
1528
|
+
}
|
|
1529
|
+
}, [svgRef]);
|
|
1530
|
+
const overlayProps = useMemo(() => {
|
|
1531
|
+
if (interactionMode === "always") return null;
|
|
1532
|
+
if (interactionMode === "modifier-key" && !hasAttemptedScroll) return null;
|
|
1533
|
+
if (interactionMode === "click-to-activate" && isActive) return null;
|
|
1534
|
+
return {
|
|
1535
|
+
mode: interactionMode,
|
|
1536
|
+
isActive,
|
|
1537
|
+
onActivate: handleActivate,
|
|
1538
|
+
width: "100%",
|
|
1539
|
+
height: "100%"
|
|
1540
|
+
};
|
|
1541
|
+
}, [interactionMode, isActive, hasAttemptedScroll, handleActivate]);
|
|
1542
|
+
const zoomIn = useCallback(() => {
|
|
1543
|
+
if (!svgRef.current) return;
|
|
1544
|
+
d32.select(svgRef.current).transition().duration(300).call(zoomBehavior.scaleBy, 1.3);
|
|
1545
|
+
}, [svgRef, zoomBehavior]);
|
|
1546
|
+
const zoomOut = useCallback(() => {
|
|
1547
|
+
if (!svgRef.current) return;
|
|
1548
|
+
d32.select(svgRef.current).transition().duration(300).call(zoomBehavior.scaleBy, 1 / 1.3);
|
|
1549
|
+
}, [svgRef, zoomBehavior]);
|
|
1550
|
+
const resetZoom = useCallback(() => {
|
|
1551
|
+
if (!svgRef.current) return;
|
|
1552
|
+
d32.select(svgRef.current).transition().duration(300).call(zoomBehavior.transform, d32.zoomIdentity);
|
|
1553
|
+
}, [svgRef, zoomBehavior]);
|
|
1554
|
+
return {
|
|
1555
|
+
isActive,
|
|
1556
|
+
overlayProps,
|
|
1557
|
+
zoomBehavior,
|
|
1558
|
+
zoomIn,
|
|
1559
|
+
zoomOut,
|
|
1560
|
+
resetZoom
|
|
1561
|
+
};
|
|
1562
|
+
}
|
|
1563
|
+
function InteractionOverlay({
|
|
1564
|
+
mode,
|
|
1565
|
+
isActive,
|
|
1566
|
+
onActivate,
|
|
1567
|
+
width,
|
|
1568
|
+
height
|
|
1569
|
+
}) {
|
|
1570
|
+
if (mode === "modifier-key") {
|
|
1571
|
+
return /* @__PURE__ */ jsx(
|
|
1572
|
+
"div",
|
|
1573
|
+
{
|
|
1574
|
+
className: "glyph-interaction-overlay",
|
|
1575
|
+
style: {
|
|
1576
|
+
...OVERLAY_BASE_STYLE,
|
|
1577
|
+
width,
|
|
1578
|
+
height,
|
|
1579
|
+
pointerEvents: "none"
|
|
1580
|
+
},
|
|
1581
|
+
"aria-hidden": "true",
|
|
1582
|
+
children: /* @__PURE__ */ jsx("div", { style: TOOLTIP_STYLE, children: /* @__PURE__ */ jsx("span", { style: TOOLTIP_TEXT_STYLE, children: "Alt + scroll to zoom" }) })
|
|
1583
|
+
}
|
|
1584
|
+
);
|
|
1585
|
+
}
|
|
1586
|
+
if (mode === "click-to-activate" && !isActive) {
|
|
1587
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1588
|
+
/* @__PURE__ */ jsx(
|
|
1589
|
+
"div",
|
|
1590
|
+
{
|
|
1591
|
+
className: "glyph-interaction-overlay",
|
|
1592
|
+
style: {
|
|
1593
|
+
...OVERLAY_BASE_STYLE,
|
|
1594
|
+
...ACTIVATION_OVERLAY_STYLE,
|
|
1595
|
+
width,
|
|
1596
|
+
height
|
|
1597
|
+
},
|
|
1598
|
+
onClick: onActivate,
|
|
1599
|
+
role: "button",
|
|
1600
|
+
tabIndex: 0,
|
|
1601
|
+
"aria-label": "Click to activate graph interaction",
|
|
1602
|
+
onKeyDown: (e) => {
|
|
1603
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
1604
|
+
e.preventDefault();
|
|
1605
|
+
onActivate();
|
|
1606
|
+
}
|
|
1607
|
+
},
|
|
1608
|
+
children: /* @__PURE__ */ jsx("div", { style: ACTIVATION_TEXT_STYLE, children: "Click to interact" })
|
|
1609
|
+
}
|
|
1610
|
+
),
|
|
1611
|
+
/* @__PURE__ */ jsx("div", { style: SR_ONLY_STYLE, role: "status", "aria-live": "polite", "aria-atomic": "true", children: "Graph interaction inactive. Click to activate." })
|
|
1612
|
+
] });
|
|
1613
|
+
}
|
|
1614
|
+
if (mode === "click-to-activate" && isActive) {
|
|
1615
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1616
|
+
/* @__PURE__ */ jsx(
|
|
1617
|
+
"div",
|
|
1618
|
+
{
|
|
1619
|
+
style: {
|
|
1620
|
+
...OVERLAY_BASE_STYLE,
|
|
1621
|
+
width,
|
|
1622
|
+
height,
|
|
1623
|
+
pointerEvents: "none",
|
|
1624
|
+
border: "2px solid var(--glyph-interaction-active-border, #0a9d7c)",
|
|
1625
|
+
borderRadius: "4px"
|
|
1626
|
+
},
|
|
1627
|
+
"aria-hidden": "true"
|
|
1628
|
+
}
|
|
1629
|
+
),
|
|
1630
|
+
/* @__PURE__ */ jsx("div", { style: SR_ONLY_STYLE, role: "status", "aria-live": "polite", "aria-atomic": "true", children: "Graph interaction active. Press Escape to deactivate." })
|
|
1631
|
+
] });
|
|
1632
|
+
}
|
|
1633
|
+
return /* @__PURE__ */ jsx(Fragment, {});
|
|
1634
|
+
}
|
|
1635
|
+
var OVERLAY_BASE_STYLE = {
|
|
1636
|
+
position: "absolute",
|
|
1637
|
+
top: 0,
|
|
1638
|
+
left: 0,
|
|
1639
|
+
display: "flex",
|
|
1640
|
+
alignItems: "center",
|
|
1641
|
+
justifyContent: "center",
|
|
1642
|
+
zIndex: 10
|
|
1643
|
+
};
|
|
1644
|
+
var TOOLTIP_STYLE = {
|
|
1645
|
+
position: "absolute",
|
|
1646
|
+
bottom: "12px",
|
|
1647
|
+
right: "12px",
|
|
1648
|
+
padding: "6px 10px",
|
|
1649
|
+
backgroundColor: "var(--glyph-interaction-tooltip-bg, rgba(26, 32, 53, 0.9))",
|
|
1650
|
+
color: "var(--glyph-interaction-tooltip-text, #f4f6fa)",
|
|
1651
|
+
borderRadius: "4px",
|
|
1652
|
+
fontSize: "12px",
|
|
1653
|
+
fontFamily: "Inter, system-ui, sans-serif",
|
|
1654
|
+
fontWeight: 500,
|
|
1655
|
+
boxShadow: "0 2px 8px rgba(0, 0, 0, 0.15)",
|
|
1656
|
+
pointerEvents: "none"
|
|
1657
|
+
};
|
|
1658
|
+
var TOOLTIP_TEXT_STYLE = {
|
|
1659
|
+
display: "flex",
|
|
1660
|
+
alignItems: "center",
|
|
1661
|
+
gap: "4px"
|
|
1662
|
+
};
|
|
1663
|
+
var ACTIVATION_OVERLAY_STYLE = {
|
|
1664
|
+
backgroundColor: "var(--glyph-interaction-overlay-bg, rgba(244, 246, 250, 0.8))",
|
|
1665
|
+
cursor: "pointer",
|
|
1666
|
+
transition: "background-color 0.2s ease"
|
|
1667
|
+
};
|
|
1668
|
+
var ACTIVATION_TEXT_STYLE = {
|
|
1669
|
+
padding: "12px 20px",
|
|
1670
|
+
backgroundColor: "var(--glyph-interaction-tooltip-bg, rgba(26, 32, 53, 0.9))",
|
|
1671
|
+
color: "var(--glyph-interaction-tooltip-text, #f4f6fa)",
|
|
1672
|
+
borderRadius: "6px",
|
|
1673
|
+
fontSize: "14px",
|
|
1674
|
+
fontFamily: "Inter, system-ui, sans-serif",
|
|
1675
|
+
fontWeight: 500,
|
|
1676
|
+
boxShadow: "0 4px 12px rgba(0, 0, 0, 0.2)"
|
|
1677
|
+
};
|
|
1678
|
+
var SR_ONLY_STYLE = {
|
|
1679
|
+
position: "absolute",
|
|
1680
|
+
width: "1px",
|
|
1681
|
+
height: "1px",
|
|
1682
|
+
padding: 0,
|
|
1683
|
+
margin: "-1px",
|
|
1684
|
+
overflow: "hidden",
|
|
1685
|
+
clip: "rect(0, 0, 0, 0)",
|
|
1686
|
+
whiteSpace: "nowrap",
|
|
1687
|
+
border: 0
|
|
1688
|
+
};
|
|
1689
|
+
function ZoomControls({ onZoomIn, onZoomOut, onReset }) {
|
|
1690
|
+
return /* @__PURE__ */ jsxs("div", { style: CONTROLS_CONTAINER_STYLE, children: [
|
|
1691
|
+
/* @__PURE__ */ jsx(
|
|
1692
|
+
"button",
|
|
1693
|
+
{
|
|
1694
|
+
onClick: onZoomIn,
|
|
1695
|
+
style: BUTTON_STYLE,
|
|
1696
|
+
"aria-label": "Zoom in",
|
|
1697
|
+
title: "Zoom in",
|
|
1698
|
+
type: "button",
|
|
1699
|
+
children: /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", children: [
|
|
1700
|
+
/* @__PURE__ */ jsx("line", { x1: "8", y1: "4", x2: "8", y2: "12", strokeWidth: "2" }),
|
|
1701
|
+
/* @__PURE__ */ jsx("line", { x1: "4", y1: "8", x2: "12", y2: "8", strokeWidth: "2" })
|
|
1702
|
+
] })
|
|
1703
|
+
}
|
|
1704
|
+
),
|
|
1705
|
+
/* @__PURE__ */ jsx(
|
|
1706
|
+
"button",
|
|
1707
|
+
{
|
|
1708
|
+
onClick: onZoomOut,
|
|
1709
|
+
style: BUTTON_STYLE,
|
|
1710
|
+
"aria-label": "Zoom out",
|
|
1711
|
+
title: "Zoom out",
|
|
1712
|
+
type: "button",
|
|
1713
|
+
children: /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", children: /* @__PURE__ */ jsx("line", { x1: "4", y1: "8", x2: "12", y2: "8", strokeWidth: "2" }) })
|
|
1714
|
+
}
|
|
1715
|
+
),
|
|
1716
|
+
/* @__PURE__ */ jsx(
|
|
1717
|
+
"button",
|
|
1718
|
+
{
|
|
1719
|
+
onClick: onReset,
|
|
1720
|
+
style: BUTTON_STYLE,
|
|
1721
|
+
"aria-label": "Reset zoom",
|
|
1722
|
+
title: "Reset zoom",
|
|
1723
|
+
type: "button",
|
|
1724
|
+
children: /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", children: /* @__PURE__ */ jsx("rect", { x: "3", y: "3", width: "10", height: "10", strokeWidth: "2", rx: "1" }) })
|
|
1725
|
+
}
|
|
1726
|
+
)
|
|
1727
|
+
] });
|
|
1728
|
+
}
|
|
1729
|
+
var CONTROLS_CONTAINER_STYLE = {
|
|
1730
|
+
position: "absolute",
|
|
1731
|
+
top: "12px",
|
|
1732
|
+
right: "12px",
|
|
1733
|
+
display: "flex",
|
|
1734
|
+
flexDirection: "column",
|
|
1735
|
+
gap: "4px",
|
|
1736
|
+
zIndex: 10
|
|
1737
|
+
};
|
|
1738
|
+
var BUTTON_STYLE = {
|
|
1739
|
+
width: "32px",
|
|
1740
|
+
height: "32px",
|
|
1741
|
+
padding: "0",
|
|
1742
|
+
display: "flex",
|
|
1743
|
+
alignItems: "center",
|
|
1744
|
+
justifyContent: "center",
|
|
1745
|
+
backgroundColor: "var(--glyph-surface-raised, #f4f6fa)",
|
|
1746
|
+
border: "1px solid var(--glyph-border, #d0d8e4)",
|
|
1747
|
+
borderRadius: "4px",
|
|
1748
|
+
color: "var(--glyph-text, #1a2035)",
|
|
1749
|
+
cursor: "pointer",
|
|
1750
|
+
transition: "all 0.2s ease",
|
|
1751
|
+
boxShadow: "0 2px 4px rgba(0, 0, 0, 0.1)"
|
|
1752
|
+
};
|
|
1433
1753
|
var GROUP_PALETTE = [
|
|
1434
1754
|
"#00d4aa",
|
|
1435
1755
|
// cyan-green
|
|
@@ -1476,7 +1796,7 @@ function getThemeVar(container, varName, fallback) {
|
|
|
1476
1796
|
return getComputedStyle(container).getPropertyValue(varName).trim() || fallback;
|
|
1477
1797
|
}
|
|
1478
1798
|
var ARROW_MARKER_ID = "glyph-graph-arrowhead";
|
|
1479
|
-
function renderGraph(svgElement, layout, groupIndex, outgoingRefs, onNavigate, onNodeClick) {
|
|
1799
|
+
function renderGraph(svgElement, layout, groupIndex, outgoingRefs, onNavigate, zoomBehavior, onNodeClick) {
|
|
1480
1800
|
const svg = d32.select(svgElement);
|
|
1481
1801
|
svg.selectAll("*").remove();
|
|
1482
1802
|
const width = Math.max(layout.width, 200);
|
|
@@ -1489,9 +1809,6 @@ function renderGraph(svgElement, layout, groupIndex, outgoingRefs, onNavigate, o
|
|
|
1489
1809
|
const nodeStrokeWidth = getThemeVar(container, "--glyph-node-stroke-width", "1.5");
|
|
1490
1810
|
const nodeFillOpacity = getThemeVar(container, "--glyph-node-fill-opacity", "0.85");
|
|
1491
1811
|
const root = svg.append("g").attr("class", "glyph-graph-root");
|
|
1492
|
-
const zoomBehavior = d32.zoom().scaleExtent([0.1, 4]).on("zoom", (event) => {
|
|
1493
|
-
root.attr("transform", event.transform.toString());
|
|
1494
|
-
});
|
|
1495
1812
|
svg.call(zoomBehavior);
|
|
1496
1813
|
const navigableNodes = /* @__PURE__ */ new Set();
|
|
1497
1814
|
const refByAnchor = /* @__PURE__ */ new Map();
|
|
@@ -1548,6 +1865,7 @@ function Graph({
|
|
|
1548
1865
|
container
|
|
1549
1866
|
}) {
|
|
1550
1867
|
const svgRef = useRef(null);
|
|
1868
|
+
const rootRef = useRef(null);
|
|
1551
1869
|
const groupIndex = useRef(/* @__PURE__ */ new Map());
|
|
1552
1870
|
const layoutResult = useMemo(() => {
|
|
1553
1871
|
const direction = resolveLayout(data);
|
|
@@ -1556,6 +1874,12 @@ function Graph({
|
|
|
1556
1874
|
}
|
|
1557
1875
|
return computeDagreLayout(data.nodes, data.edges, direction);
|
|
1558
1876
|
}, [data]);
|
|
1877
|
+
const { overlayProps, zoomBehavior, zoomIn, zoomOut, resetZoom } = useZoomInteraction({
|
|
1878
|
+
svgRef,
|
|
1879
|
+
rootRef,
|
|
1880
|
+
interactionMode: data.interactionMode ?? "modifier-key",
|
|
1881
|
+
blockId: block.id
|
|
1882
|
+
});
|
|
1559
1883
|
const handleNodeClick = useMemo(() => {
|
|
1560
1884
|
if (!onInteraction) return void 0;
|
|
1561
1885
|
return (nodeId, nodeLabel) => {
|
|
@@ -1576,27 +1900,36 @@ function Graph({
|
|
|
1576
1900
|
groupIndex.current,
|
|
1577
1901
|
outgoingRefs,
|
|
1578
1902
|
onNavigate,
|
|
1903
|
+
zoomBehavior,
|
|
1579
1904
|
handleNodeClick
|
|
1580
1905
|
);
|
|
1581
|
-
|
|
1906
|
+
const rootElement = svgRef.current.querySelector(".glyph-graph-root");
|
|
1907
|
+
if (rootElement) {
|
|
1908
|
+
rootRef.current = rootElement;
|
|
1909
|
+
}
|
|
1910
|
+
}, [layoutResult, outgoingRefs, onNavigate, zoomBehavior, handleNodeClick]);
|
|
1582
1911
|
const ariaLabel = `${data.type} graph with ${data.nodes.length} nodes and ${data.edges.length} edges`;
|
|
1583
1912
|
return /* @__PURE__ */ jsxs("div", { className: "glyph-graph-container", children: [
|
|
1584
|
-
/* @__PURE__ */
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1913
|
+
/* @__PURE__ */ jsxs("div", { style: { position: "relative" }, children: [
|
|
1914
|
+
/* @__PURE__ */ jsx(
|
|
1915
|
+
"svg",
|
|
1916
|
+
{
|
|
1917
|
+
ref: svgRef,
|
|
1918
|
+
role: "img",
|
|
1919
|
+
"aria-label": ariaLabel,
|
|
1920
|
+
width: "100%",
|
|
1921
|
+
height: "100%",
|
|
1922
|
+
style: {
|
|
1923
|
+
minHeight: container.tier === "compact" ? 200 : 300,
|
|
1924
|
+
maxHeight: container.tier === "compact" ? 500 : 700,
|
|
1925
|
+
display: "block"
|
|
1926
|
+
}
|
|
1596
1927
|
}
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1928
|
+
),
|
|
1929
|
+
overlayProps && /* @__PURE__ */ jsx(InteractionOverlay, { ...overlayProps }),
|
|
1930
|
+
/* @__PURE__ */ jsx(ZoomControls, { onZoomIn: zoomIn, onZoomOut: zoomOut, onReset: resetZoom })
|
|
1931
|
+
] }),
|
|
1932
|
+
/* @__PURE__ */ jsxs("table", { className: "sr-only", "aria-label": "Graph data", style: SR_ONLY_STYLE2, children: [
|
|
1600
1933
|
/* @__PURE__ */ jsx("caption", { children: "Graph nodes and connections" }),
|
|
1601
1934
|
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
|
|
1602
1935
|
/* @__PURE__ */ jsx("th", { scope: "col", children: "Node" }),
|
|
@@ -1618,7 +1951,7 @@ function Graph({
|
|
|
1618
1951
|
] })
|
|
1619
1952
|
] });
|
|
1620
1953
|
}
|
|
1621
|
-
var
|
|
1954
|
+
var SR_ONLY_STYLE2 = {
|
|
1622
1955
|
position: "absolute",
|
|
1623
1956
|
width: "1px",
|
|
1624
1957
|
height: "1px",
|
|
@@ -1742,16 +2075,13 @@ function drawCrowsFoot(g, x, y, angle, symbol) {
|
|
|
1742
2075
|
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)");
|
|
1743
2076
|
}
|
|
1744
2077
|
}
|
|
1745
|
-
function renderRelation(svgElement, layout) {
|
|
2078
|
+
function renderRelation(svgElement, layout, zoomBehavior) {
|
|
1746
2079
|
const svg = d32.select(svgElement);
|
|
1747
2080
|
svg.selectAll("*").remove();
|
|
1748
2081
|
const width = Math.max(layout.width, 200);
|
|
1749
2082
|
const height = Math.max(layout.height, 200);
|
|
1750
2083
|
svg.attr("viewBox", `0 0 ${width} ${height}`);
|
|
1751
2084
|
const root = svg.append("g").attr("class", "glyph-relation-root");
|
|
1752
|
-
const zoomBehavior = d32.zoom().scaleExtent([0.1, 4]).on("zoom", (event) => {
|
|
1753
|
-
root.attr("transform", event.transform.toString());
|
|
1754
|
-
});
|
|
1755
2085
|
svg.call(zoomBehavior);
|
|
1756
2086
|
const entityMap = /* @__PURE__ */ new Map();
|
|
1757
2087
|
for (const entity of layout.entities) {
|
|
@@ -1823,29 +2153,44 @@ function renderRelation(svgElement, layout) {
|
|
|
1823
2153
|
}
|
|
1824
2154
|
}
|
|
1825
2155
|
}
|
|
1826
|
-
function Relation({ data }) {
|
|
2156
|
+
function Relation({ data, block }) {
|
|
1827
2157
|
const svgRef = useRef(null);
|
|
2158
|
+
const rootRef = useRef(null);
|
|
1828
2159
|
const layoutResult = useMemo(() => {
|
|
1829
2160
|
return computeRelationLayout(data);
|
|
1830
2161
|
}, [data]);
|
|
2162
|
+
const { overlayProps, zoomBehavior, zoomIn, zoomOut, resetZoom } = useZoomInteraction({
|
|
2163
|
+
svgRef,
|
|
2164
|
+
rootRef,
|
|
2165
|
+
interactionMode: data.interactionMode ?? "modifier-key",
|
|
2166
|
+
blockId: block.id
|
|
2167
|
+
});
|
|
1831
2168
|
useEffect(() => {
|
|
1832
2169
|
if (!svgRef.current) return;
|
|
1833
|
-
renderRelation(svgRef.current, layoutResult);
|
|
1834
|
-
|
|
2170
|
+
renderRelation(svgRef.current, layoutResult, zoomBehavior);
|
|
2171
|
+
const rootElement = svgRef.current.querySelector(".glyph-relation-root");
|
|
2172
|
+
if (rootElement) {
|
|
2173
|
+
rootRef.current = rootElement;
|
|
2174
|
+
}
|
|
2175
|
+
}, [layoutResult, zoomBehavior]);
|
|
1835
2176
|
const ariaLabel = `Entity-relationship diagram with ${data.entities.length} entities and ${data.relationships.length} relationships`;
|
|
1836
2177
|
return /* @__PURE__ */ jsxs("div", { className: "glyph-relation-container", children: [
|
|
1837
|
-
/* @__PURE__ */
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
2178
|
+
/* @__PURE__ */ jsxs("div", { style: { position: "relative" }, children: [
|
|
2179
|
+
/* @__PURE__ */ jsx(
|
|
2180
|
+
"svg",
|
|
2181
|
+
{
|
|
2182
|
+
ref: svgRef,
|
|
2183
|
+
role: "img",
|
|
2184
|
+
"aria-label": ariaLabel,
|
|
2185
|
+
width: "100%",
|
|
2186
|
+
height: "100%",
|
|
2187
|
+
style: { minHeight: 300, maxHeight: 700, display: "block" }
|
|
2188
|
+
}
|
|
2189
|
+
),
|
|
2190
|
+
overlayProps && /* @__PURE__ */ jsx(InteractionOverlay, { ...overlayProps }),
|
|
2191
|
+
/* @__PURE__ */ jsx(ZoomControls, { onZoomIn: zoomIn, onZoomOut: zoomOut, onReset: resetZoom })
|
|
2192
|
+
] }),
|
|
2193
|
+
/* @__PURE__ */ jsxs("table", { className: "sr-only", "aria-label": "Entity-relationship data", style: SR_ONLY_STYLE3, children: [
|
|
1849
2194
|
/* @__PURE__ */ jsx("caption", { children: "Entities and relationships" }),
|
|
1850
2195
|
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
|
|
1851
2196
|
/* @__PURE__ */ jsx("th", { scope: "col", children: "Entity" }),
|
|
@@ -1868,7 +2213,7 @@ function Relation({ data }) {
|
|
|
1868
2213
|
] })
|
|
1869
2214
|
] });
|
|
1870
2215
|
}
|
|
1871
|
-
var
|
|
2216
|
+
var SR_ONLY_STYLE3 = {
|
|
1872
2217
|
position: "absolute",
|
|
1873
2218
|
width: "1px",
|
|
1874
2219
|
height: "1px",
|
|
@@ -2504,7 +2849,7 @@ function renderNodeShape(nodeG, node, fillOpacity, strokeWidth) {
|
|
|
2504
2849
|
}
|
|
2505
2850
|
}
|
|
2506
2851
|
}
|
|
2507
|
-
function renderFlowchart(svgElement, layout) {
|
|
2852
|
+
function renderFlowchart(svgElement, layout, zoomBehavior) {
|
|
2508
2853
|
const svg = d32.select(svgElement);
|
|
2509
2854
|
svg.selectAll("*").remove();
|
|
2510
2855
|
const width = Math.max(layout.width, 200);
|
|
@@ -2516,9 +2861,6 @@ function renderFlowchart(svgElement, layout) {
|
|
|
2516
2861
|
const nodeStrokeWidth = getThemeVar2(container, "--glyph-node-stroke-width", "1.5");
|
|
2517
2862
|
const nodeFillOpacity = getThemeVar2(container, "--glyph-node-fill-opacity", "0.85");
|
|
2518
2863
|
const root = svg.append("g").attr("class", "glyph-flowchart-root");
|
|
2519
|
-
const zoomBehavior = d32.zoom().scaleExtent([0.1, 4]).on("zoom", (event) => {
|
|
2520
|
-
root.attr("transform", event.transform.toString());
|
|
2521
|
-
});
|
|
2522
2864
|
svg.call(zoomBehavior);
|
|
2523
2865
|
const lineGen = d32.line().x((d) => d.x).y((d) => d.y).curve(d32.curveBasis);
|
|
2524
2866
|
const edgeGroup = root.append("g").attr("class", "glyph-flowchart-edges");
|
|
@@ -2539,13 +2881,28 @@ function renderFlowchart(svgElement, layout) {
|
|
|
2539
2881
|
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);
|
|
2540
2882
|
}
|
|
2541
2883
|
}
|
|
2542
|
-
function Flowchart({
|
|
2884
|
+
function Flowchart({
|
|
2885
|
+
data,
|
|
2886
|
+
block,
|
|
2887
|
+
container
|
|
2888
|
+
}) {
|
|
2543
2889
|
const svgRef = useRef(null);
|
|
2890
|
+
const rootRef = useRef(null);
|
|
2544
2891
|
const layoutResult = useMemo(() => computeLayout(data.nodes, data.edges, data.direction), [data]);
|
|
2892
|
+
const { overlayProps, zoomBehavior, zoomIn, zoomOut, resetZoom } = useZoomInteraction({
|
|
2893
|
+
svgRef,
|
|
2894
|
+
rootRef,
|
|
2895
|
+
interactionMode: data.interactionMode ?? "modifier-key",
|
|
2896
|
+
blockId: block.id
|
|
2897
|
+
});
|
|
2545
2898
|
useEffect(() => {
|
|
2546
2899
|
if (!svgRef.current) return;
|
|
2547
|
-
renderFlowchart(svgRef.current, layoutResult);
|
|
2548
|
-
|
|
2900
|
+
renderFlowchart(svgRef.current, layoutResult, zoomBehavior);
|
|
2901
|
+
const rootElement = svgRef.current.querySelector(".glyph-flowchart-root");
|
|
2902
|
+
if (rootElement) {
|
|
2903
|
+
rootRef.current = rootElement;
|
|
2904
|
+
}
|
|
2905
|
+
}, [layoutResult, zoomBehavior]);
|
|
2549
2906
|
const nodeCount = data.nodes.length;
|
|
2550
2907
|
const edgeCount = data.edges.length;
|
|
2551
2908
|
const ariaLabel = data.title ? `${data.title}: flowchart with ${nodeCount} nodes and ${edgeCount} edges` : `Flowchart with ${nodeCount} nodes and ${edgeCount} edges`;
|
|
@@ -2563,22 +2920,26 @@ function Flowchart({ data, container }) {
|
|
|
2563
2920
|
children: data.title
|
|
2564
2921
|
}
|
|
2565
2922
|
),
|
|
2566
|
-
/* @__PURE__ */
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2923
|
+
/* @__PURE__ */ jsxs("div", { style: { position: "relative" }, children: [
|
|
2924
|
+
/* @__PURE__ */ jsx(
|
|
2925
|
+
"svg",
|
|
2926
|
+
{
|
|
2927
|
+
ref: svgRef,
|
|
2928
|
+
role: "img",
|
|
2929
|
+
"aria-label": ariaLabel,
|
|
2930
|
+
width: "100%",
|
|
2931
|
+
height: "100%",
|
|
2932
|
+
style: {
|
|
2933
|
+
minHeight: container.tier === "compact" ? 200 : 300,
|
|
2934
|
+
maxHeight: container.tier === "compact" ? 500 : 700,
|
|
2935
|
+
display: "block"
|
|
2936
|
+
}
|
|
2578
2937
|
}
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2938
|
+
),
|
|
2939
|
+
overlayProps && /* @__PURE__ */ jsx(InteractionOverlay, { ...overlayProps }),
|
|
2940
|
+
/* @__PURE__ */ jsx(ZoomControls, { onZoomIn: zoomIn, onZoomOut: zoomOut, onReset: resetZoom })
|
|
2941
|
+
] }),
|
|
2942
|
+
/* @__PURE__ */ jsxs("table", { className: "sr-only", "aria-label": "Flowchart data", style: SR_ONLY_STYLE4, children: [
|
|
2582
2943
|
/* @__PURE__ */ jsx("caption", { children: "Flowchart nodes and connections" }),
|
|
2583
2944
|
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
|
|
2584
2945
|
/* @__PURE__ */ jsx("th", { scope: "col", children: "Node" }),
|
|
@@ -2600,7 +2961,7 @@ function Flowchart({ data, container }) {
|
|
|
2600
2961
|
] })
|
|
2601
2962
|
] });
|
|
2602
2963
|
}
|
|
2603
|
-
var
|
|
2964
|
+
var SR_ONLY_STYLE4 = {
|
|
2604
2965
|
position: "absolute",
|
|
2605
2966
|
width: "1px",
|
|
2606
2967
|
height: "1px",
|
|
@@ -3222,7 +3583,7 @@ function Sequence({ data, container }) {
|
|
|
3222
3583
|
]
|
|
3223
3584
|
}
|
|
3224
3585
|
),
|
|
3225
|
-
/* @__PURE__ */ jsxs("table", { className: "sr-only", "aria-label": "Sequence data", style:
|
|
3586
|
+
/* @__PURE__ */ jsxs("table", { className: "sr-only", "aria-label": "Sequence data", style: SR_ONLY_STYLE5, children: [
|
|
3226
3587
|
/* @__PURE__ */ jsx("caption", { children: "Sequence messages in order" }),
|
|
3227
3588
|
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
|
|
3228
3589
|
/* @__PURE__ */ jsx("th", { scope: "col", children: "#" }),
|
|
@@ -3245,7 +3606,7 @@ function Sequence({ data, container }) {
|
|
|
3245
3606
|
] })
|
|
3246
3607
|
] });
|
|
3247
3608
|
}
|
|
3248
|
-
var
|
|
3609
|
+
var SR_ONLY_STYLE5 = {
|
|
3249
3610
|
position: "absolute",
|
|
3250
3611
|
width: "1px",
|
|
3251
3612
|
height: "1px",
|
|
@@ -3651,7 +4012,7 @@ function Architecture({
|
|
|
3651
4012
|
}
|
|
3652
4013
|
}
|
|
3653
4014
|
),
|
|
3654
|
-
/* @__PURE__ */ jsxs("table", { className: "sr-only", "aria-label": "Architecture data", style:
|
|
4015
|
+
/* @__PURE__ */ jsxs("table", { className: "sr-only", "aria-label": "Architecture data", style: SR_ONLY_STYLE6, children: [
|
|
3655
4016
|
/* @__PURE__ */ jsx("caption", { children: "Architecture nodes and connections" }),
|
|
3656
4017
|
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
|
|
3657
4018
|
/* @__PURE__ */ jsx("th", { scope: "col", children: "Node" }),
|
|
@@ -3695,7 +4056,7 @@ function countLeafNodes(children) {
|
|
|
3695
4056
|
}
|
|
3696
4057
|
return count;
|
|
3697
4058
|
}
|
|
3698
|
-
var
|
|
4059
|
+
var SR_ONLY_STYLE6 = {
|
|
3699
4060
|
position: "absolute",
|
|
3700
4061
|
width: "1px",
|
|
3701
4062
|
height: "1px",
|
|
@@ -3883,7 +4244,7 @@ function layoutTree(data) {
|
|
|
3883
4244
|
};
|
|
3884
4245
|
}
|
|
3885
4246
|
function renderAccessibleList(root, children) {
|
|
3886
|
-
return /* @__PURE__ */ jsx("ul", { style:
|
|
4247
|
+
return /* @__PURE__ */ jsx("ul", { style: SR_ONLY_STYLE7, role: "list", "aria-label": "Mind map structure", children: /* @__PURE__ */ jsxs("li", { children: [
|
|
3887
4248
|
root,
|
|
3888
4249
|
children.length > 0 && renderAccessibleChildren(children)
|
|
3889
4250
|
] }) });
|
|
@@ -4010,7 +4371,7 @@ function MindMap({ data, container }) {
|
|
|
4010
4371
|
renderAccessibleList(data.root, data.children)
|
|
4011
4372
|
] });
|
|
4012
4373
|
}
|
|
4013
|
-
var
|
|
4374
|
+
var SR_ONLY_STYLE7 = {
|
|
4014
4375
|
position: "absolute",
|
|
4015
4376
|
width: "1px",
|
|
4016
4377
|
height: "1px",
|