@hex-core/components 1.7.0 → 1.8.1
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/_tsup-dts-rollup.d.ts +1566 -5
- package/dist/arc.d.ts +4 -0
- package/dist/arc.js +147 -0
- package/dist/arc.js.map +1 -0
- package/dist/audio-player.d.ts +2 -0
- package/dist/audio-player.js +119 -0
- package/dist/audio-player.js.map +1 -0
- package/dist/audio-waveform.d.ts +2 -0
- package/dist/audio-waveform.js +72 -0
- package/dist/audio-waveform.js.map +1 -0
- package/dist/canvas.d.ts +2 -0
- package/dist/canvas.js +73 -0
- package/dist/canvas.js.map +1 -0
- package/dist/chord.d.ts +4 -0
- package/dist/chord.js +230 -0
- package/dist/chord.js.map +1 -0
- package/dist/cloze.d.ts +3 -0
- package/dist/cloze.js +98 -0
- package/dist/cloze.js.map +1 -0
- package/dist/color-picker.js.map +1 -1
- package/dist/compare-table.d.ts +4 -0
- package/dist/compare-table.js +109 -0
- package/dist/compare-table.js.map +1 -0
- package/dist/data-table.js.map +1 -1
- package/dist/deck.d.ts +3 -0
- package/dist/deck.js +231 -0
- package/dist/deck.js.map +1 -0
- package/dist/dendrogram.d.ts +3 -0
- package/dist/dendrogram.js +162 -0
- package/dist/dendrogram.js.map +1 -0
- package/dist/diagram.d.ts +2 -0
- package/dist/diagram.js +70 -0
- package/dist/diagram.js.map +1 -0
- package/dist/flashcard.d.ts +2 -0
- package/dist/flashcard.js +107 -0
- package/dist/flashcard.js.map +1 -0
- package/dist/flowchart.d.ts +4 -0
- package/dist/flowchart.js +275 -0
- package/dist/flowchart.js.map +1 -0
- package/dist/funnel.d.ts +3 -0
- package/dist/funnel.js +157 -0
- package/dist/funnel.js.map +1 -0
- package/dist/gantt.d.ts +3 -0
- package/dist/gantt.js +279 -0
- package/dist/gantt.js.map +1 -0
- package/dist/image-occlusion.d.ts +3 -0
- package/dist/image-occlusion.js +106 -0
- package/dist/image-occlusion.js.map +1 -0
- package/dist/index.d.ts +84 -0
- package/dist/index.js +3946 -2
- package/dist/index.js.map +1 -1
- package/dist/matrix.d.ts +3 -0
- package/dist/matrix.js +155 -0
- package/dist/matrix.js.map +1 -0
- package/dist/mind-map.d.ts +3 -0
- package/dist/mind-map.js +167 -0
- package/dist/mind-map.js.map +1 -0
- package/dist/org-chart.d.ts +3 -0
- package/dist/org-chart.js +215 -0
- package/dist/org-chart.js.map +1 -0
- package/dist/pyramid.d.ts +3 -0
- package/dist/pyramid.js +150 -0
- package/dist/pyramid.js.map +1 -0
- package/dist/quiz.d.ts +3 -0
- package/dist/quiz.js +128 -0
- package/dist/quiz.js.map +1 -0
- package/dist/sankey.d.ts +4 -0
- package/dist/sankey.js +190 -0
- package/dist/sankey.js.map +1 -0
- package/dist/schemas.d.ts +23 -0
- package/dist/schemas.js +2210 -3
- package/dist/schemas.js.map +1 -1
- package/dist/sequence.d.ts +4 -0
- package/dist/sequence.js +229 -0
- package/dist/sequence.js.map +1 -0
- package/dist/sonner.js.map +1 -1
- package/dist/spaced-repetition.d.ts +3 -0
- package/dist/spaced-repetition.js +73 -0
- package/dist/spaced-repetition.js.map +1 -0
- package/dist/sunburst.d.ts +3 -0
- package/dist/sunburst.js +205 -0
- package/dist/sunburst.js.map +1 -0
- package/dist/terminal.d.ts +2 -0
- package/dist/terminal.js +153 -0
- package/dist/terminal.js.map +1 -0
- package/dist/textarea.js.map +1 -1
- package/dist/time-axis.d.ts +3 -0
- package/dist/time-axis.js +233 -0
- package/dist/time-axis.js.map +1 -0
- package/dist/tool-call.js +6 -1
- package/dist/tool-call.js.map +1 -1
- package/dist/tree-map.d.ts +3 -0
- package/dist/tree-map.js +171 -0
- package/dist/tree-map.js.map +1 -0
- package/dist/venn.d.ts +3 -0
- package/dist/venn.js +196 -0
- package/dist/venn.js.map +1 -0
- package/package.json +49 -5
package/dist/matrix.d.ts
ADDED
package/dist/matrix.js
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { clsx } from 'clsx';
|
|
4
|
+
import { twMerge } from 'tailwind-merge';
|
|
5
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
6
|
+
|
|
7
|
+
function cn(...inputs) {
|
|
8
|
+
return twMerge(clsx(inputs));
|
|
9
|
+
}
|
|
10
|
+
var MIN_CELL_SIZE_FOR_VALUE = 28;
|
|
11
|
+
function Matrix({
|
|
12
|
+
nodes,
|
|
13
|
+
matrix,
|
|
14
|
+
size = 480,
|
|
15
|
+
labelMargin = 80,
|
|
16
|
+
showValues = true,
|
|
17
|
+
onCellHover,
|
|
18
|
+
onCellClick,
|
|
19
|
+
className,
|
|
20
|
+
...rest
|
|
21
|
+
}) {
|
|
22
|
+
const cells = React.useMemo(() => layout(nodes, matrix, size, labelMargin), [nodes, matrix, size, labelMargin]);
|
|
23
|
+
const desc = `Matrix with ${nodes.length} node${nodes.length === 1 ? "" : "s"} (${nodes.length}\xD7${nodes.length} cells)`;
|
|
24
|
+
const cellSize = nodes.length > 0 ? (size - labelMargin) / nodes.length : 0;
|
|
25
|
+
return /* @__PURE__ */ jsxs(
|
|
26
|
+
"svg",
|
|
27
|
+
{
|
|
28
|
+
...rest,
|
|
29
|
+
"data-hex-matrix": true,
|
|
30
|
+
role: "img",
|
|
31
|
+
width: size,
|
|
32
|
+
height: size,
|
|
33
|
+
viewBox: `0 0 ${size} ${size}`,
|
|
34
|
+
className: cn("block", className),
|
|
35
|
+
children: [
|
|
36
|
+
/* @__PURE__ */ jsx("title", { children: "Adjacency matrix" }),
|
|
37
|
+
/* @__PURE__ */ jsx("desc", { children: desc }),
|
|
38
|
+
/* @__PURE__ */ jsx("g", { "data-hex-matrix-rows": true, children: nodes.map((n, i) => /* @__PURE__ */ jsx(
|
|
39
|
+
"text",
|
|
40
|
+
{
|
|
41
|
+
x: labelMargin - 4,
|
|
42
|
+
y: labelMargin + cellSize * i + cellSize / 2,
|
|
43
|
+
dy: "0.35em",
|
|
44
|
+
textAnchor: "end",
|
|
45
|
+
fontSize: 10,
|
|
46
|
+
fill: "hsl(var(--foreground))",
|
|
47
|
+
children: n.label
|
|
48
|
+
},
|
|
49
|
+
`row-${n.id}`
|
|
50
|
+
)) }),
|
|
51
|
+
/* @__PURE__ */ jsx("g", { "data-hex-matrix-cols": true, children: nodes.map((n, i) => /* @__PURE__ */ jsx(
|
|
52
|
+
"text",
|
|
53
|
+
{
|
|
54
|
+
x: labelMargin + cellSize * i + cellSize / 2,
|
|
55
|
+
y: labelMargin - 4,
|
|
56
|
+
textAnchor: "end",
|
|
57
|
+
fontSize: 10,
|
|
58
|
+
fill: "hsl(var(--foreground))",
|
|
59
|
+
transform: `rotate(-45 ${labelMargin + cellSize * i + cellSize / 2} ${labelMargin - 4})`,
|
|
60
|
+
children: n.label
|
|
61
|
+
},
|
|
62
|
+
`col-${n.id}`
|
|
63
|
+
)) }),
|
|
64
|
+
/* @__PURE__ */ jsx("g", { "data-hex-matrix-cells": true, children: cells.map((c) => {
|
|
65
|
+
const interactive = Boolean(onCellHover || onCellClick);
|
|
66
|
+
const cellPayload = { row: c.row, col: c.col, value: c.value };
|
|
67
|
+
const fireHover = (cell) => onCellHover?.(cell);
|
|
68
|
+
const handleActivate = () => onCellClick?.(cellPayload);
|
|
69
|
+
return /* @__PURE__ */ jsxs(
|
|
70
|
+
"g",
|
|
71
|
+
{
|
|
72
|
+
"data-hex-matrix-cell": true,
|
|
73
|
+
"data-row": c.rowIndex,
|
|
74
|
+
"data-col": c.colIndex,
|
|
75
|
+
role: interactive ? "button" : void 0,
|
|
76
|
+
tabIndex: interactive ? 0 : void 0,
|
|
77
|
+
"aria-label": interactive ? `${c.row.label} \u2192 ${c.col.label}: ${c.value}` : void 0,
|
|
78
|
+
style: interactive ? { cursor: "pointer" } : void 0,
|
|
79
|
+
onMouseEnter: onCellHover ? () => fireHover(cellPayload) : void 0,
|
|
80
|
+
onMouseLeave: onCellHover ? () => fireHover(null) : void 0,
|
|
81
|
+
onFocus: onCellHover ? () => fireHover(cellPayload) : void 0,
|
|
82
|
+
onBlur: onCellHover ? () => fireHover(null) : void 0,
|
|
83
|
+
onClick: onCellClick ? handleActivate : void 0,
|
|
84
|
+
onKeyDown: onCellClick ? (e) => activateOnKey(e, handleActivate) : void 0,
|
|
85
|
+
children: [
|
|
86
|
+
/* @__PURE__ */ jsx(
|
|
87
|
+
"rect",
|
|
88
|
+
{
|
|
89
|
+
x: c.x,
|
|
90
|
+
y: c.y,
|
|
91
|
+
width: cellSize,
|
|
92
|
+
height: cellSize,
|
|
93
|
+
fill: "hsl(var(--chart-1, var(--primary)))",
|
|
94
|
+
fillOpacity: 0.08 + 0.87 * c.intensity,
|
|
95
|
+
stroke: "hsl(var(--background))",
|
|
96
|
+
strokeWidth: 0.5
|
|
97
|
+
}
|
|
98
|
+
),
|
|
99
|
+
showValues && cellSize > MIN_CELL_SIZE_FOR_VALUE && c.value !== 0 ? /* @__PURE__ */ jsx(
|
|
100
|
+
"text",
|
|
101
|
+
{
|
|
102
|
+
x: c.x + cellSize / 2,
|
|
103
|
+
y: c.y + cellSize / 2,
|
|
104
|
+
dy: "0.35em",
|
|
105
|
+
textAnchor: "middle",
|
|
106
|
+
fontSize: 9,
|
|
107
|
+
fill: c.intensity > 0.55 ? "hsl(var(--background))" : "hsl(var(--foreground))",
|
|
108
|
+
style: { pointerEvents: "none" },
|
|
109
|
+
children: c.value
|
|
110
|
+
}
|
|
111
|
+
) : null
|
|
112
|
+
]
|
|
113
|
+
},
|
|
114
|
+
`${c.rowIndex}-${c.colIndex}`
|
|
115
|
+
);
|
|
116
|
+
}) })
|
|
117
|
+
]
|
|
118
|
+
}
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
function layout(nodes, matrix, size, labelMargin) {
|
|
122
|
+
if (nodes.length === 0) return [];
|
|
123
|
+
const cellSize = (size - labelMargin) / nodes.length;
|
|
124
|
+
let maxValue = 0;
|
|
125
|
+
for (const row of matrix) {
|
|
126
|
+
for (const v of row) if (v > maxValue) maxValue = v;
|
|
127
|
+
}
|
|
128
|
+
const cells = [];
|
|
129
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
130
|
+
for (let j = 0; j < nodes.length; j++) {
|
|
131
|
+
const value = matrix[i]?.[j] ?? 0;
|
|
132
|
+
cells.push({
|
|
133
|
+
row: nodes[i],
|
|
134
|
+
col: nodes[j],
|
|
135
|
+
rowIndex: i,
|
|
136
|
+
colIndex: j,
|
|
137
|
+
value,
|
|
138
|
+
x: labelMargin + cellSize * j,
|
|
139
|
+
y: labelMargin + cellSize * i,
|
|
140
|
+
intensity: maxValue > 0 ? value / maxValue : 0
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return cells;
|
|
145
|
+
}
|
|
146
|
+
function activateOnKey(e, fn) {
|
|
147
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
148
|
+
e.preventDefault();
|
|
149
|
+
fn();
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export { Matrix };
|
|
154
|
+
//# sourceMappingURL=matrix.js.map
|
|
155
|
+
//# sourceMappingURL=matrix.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/utils.ts","../src/artifacts/matrix/matrix.tsx"],"names":[],"mappings":"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","file":"matrix.js","sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Merge class names with Tailwind CSS conflict resolution.\n * @param inputs - Class values (strings, arrays, objects) to merge\n * @returns A single merged class string with Tailwind conflicts resolved\n */\nexport function cn(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\n/**\n * Adjacency-matrix diagram. Square grid where cell (row i, col j)\n * encodes the relationship from node i to node j by color intensity.\n * Pure SVG; no heavy peer dependency. Best for dense graphs where\n * node-link diagrams turn into \"hairballs\" — Matrix scales gracefully\n * to hundreds of nodes if the SVG is sized to match.\n *\n * @example\n * <Matrix\n * nodes={[\"A\", \"B\", \"C\", \"D\"].map((id) => ({ id, label: id }))}\n * matrix={[\n * [0, 5, 8, 1],\n * [3, 0, 2, 4],\n * [6, 0, 0, 7],\n * [2, 1, 9, 0],\n * ]}\n * />\n */\nexport type MatrixNode = {\n\tid: string;\n\tlabel: string;\n};\n\nexport interface MatrixProps extends Omit<React.SVGAttributes<SVGSVGElement>, \"children\"> {\n\t/** Nodes — rows AND columns. Order matches `matrix` rows/columns. */\n\tnodes: MatrixNode[];\n\t/** Square N×N matrix of values. matrix[i][j] = relationship from node i to node j. */\n\tmatrix: number[][];\n\t/** Pixel size of the rendered SVG (it's square). Default 480. */\n\tsize?: number;\n\t/** Pixel reserved for row/column labels along the edges. Default 80. */\n\tlabelMargin?: number;\n\t/** Show numeric values inside cells when the cell is large enough. Default true. */\n\tshowValues?: boolean;\n\t/** Fired when a cell is hovered (or hover ends, with `null`). */\n\tonCellHover?: (cell: { row: MatrixNode; col: MatrixNode; value: number } | null) => void;\n\t/** Fired when a cell is clicked. */\n\tonCellClick?: (cell: { row: MatrixNode; col: MatrixNode; value: number }) => void;\n}\n\ninterface LaidOutCell {\n\trow: MatrixNode;\n\tcol: MatrixNode;\n\trowIndex: number;\n\tcolIndex: number;\n\tvalue: number;\n\tx: number;\n\ty: number;\n\tintensity: number;\n}\n\n/** Below this px size, in-cell numeric labels become unreadable and are hidden. */\nconst MIN_CELL_SIZE_FOR_VALUE = 28;\n\nfunction Matrix({\n\tnodes,\n\tmatrix,\n\tsize = 480,\n\tlabelMargin = 80,\n\tshowValues = true,\n\tonCellHover,\n\tonCellClick,\n\tclassName,\n\t...rest\n}: MatrixProps) {\n\tconst cells = React.useMemo(() => layout(nodes, matrix, size, labelMargin), [nodes, matrix, size, labelMargin]);\n\tconst desc = `Matrix with ${nodes.length} node${nodes.length === 1 ? \"\" : \"s\"} (${nodes.length}×${nodes.length} cells)`;\n\tconst cellSize = nodes.length > 0 ? (size - labelMargin) / nodes.length : 0;\n\n\treturn (\n\t\t<svg\n\t\t\t{...rest}\n\t\t\tdata-hex-matrix\n\t\t\trole=\"img\"\n\t\t\twidth={size}\n\t\t\theight={size}\n\t\t\tviewBox={`0 0 ${size} ${size}`}\n\t\t\tclassName={cn(\"block\", className)}\n\t\t>\n\t\t\t<title>Adjacency matrix</title>\n\t\t\t<desc>{desc}</desc>\n\t\t\t<g data-hex-matrix-rows>\n\t\t\t\t{nodes.map((n, i) => (\n\t\t\t\t\t<text\n\t\t\t\t\t\tkey={`row-${n.id}`}\n\t\t\t\t\t\tx={labelMargin - 4}\n\t\t\t\t\t\ty={labelMargin + cellSize * i + cellSize / 2}\n\t\t\t\t\t\tdy=\"0.35em\"\n\t\t\t\t\t\ttextAnchor=\"end\"\n\t\t\t\t\t\tfontSize={10}\n\t\t\t\t\t\tfill=\"hsl(var(--foreground))\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{n.label}\n\t\t\t\t\t</text>\n\t\t\t\t))}\n\t\t\t</g>\n\t\t\t<g data-hex-matrix-cols>\n\t\t\t\t{nodes.map((n, i) => (\n\t\t\t\t\t<text\n\t\t\t\t\t\tkey={`col-${n.id}`}\n\t\t\t\t\t\tx={labelMargin + cellSize * i + cellSize / 2}\n\t\t\t\t\t\ty={labelMargin - 4}\n\t\t\t\t\t\ttextAnchor=\"end\"\n\t\t\t\t\t\tfontSize={10}\n\t\t\t\t\t\tfill=\"hsl(var(--foreground))\"\n\t\t\t\t\t\ttransform={`rotate(-45 ${labelMargin + cellSize * i + cellSize / 2} ${labelMargin - 4})`}\n\t\t\t\t\t>\n\t\t\t\t\t\t{n.label}\n\t\t\t\t\t</text>\n\t\t\t\t))}\n\t\t\t</g>\n\t\t\t<g data-hex-matrix-cells>\n\t\t\t\t{cells.map((c) => {\n\t\t\t\t\tconst interactive = Boolean(onCellHover || onCellClick);\n\t\t\t\t\tconst cellPayload = { row: c.row, col: c.col, value: c.value };\n\t\t\t\t\tconst fireHover = (cell: typeof cellPayload | null) => onCellHover?.(cell);\n\t\t\t\t\tconst handleActivate = () => onCellClick?.(cellPayload);\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<g\n\t\t\t\t\t\t\tkey={`${c.rowIndex}-${c.colIndex}`}\n\t\t\t\t\t\t\tdata-hex-matrix-cell\n\t\t\t\t\t\t\tdata-row={c.rowIndex}\n\t\t\t\t\t\t\tdata-col={c.colIndex}\n\t\t\t\t\t\t\trole={interactive ? \"button\" : undefined}\n\t\t\t\t\t\t\ttabIndex={interactive ? 0 : undefined}\n\t\t\t\t\t\t\taria-label={interactive ? `${c.row.label} → ${c.col.label}: ${c.value}` : undefined}\n\t\t\t\t\t\t\tstyle={interactive ? { cursor: \"pointer\" } : undefined}\n\t\t\t\t\t\t\tonMouseEnter={onCellHover ? () => fireHover(cellPayload) : undefined}\n\t\t\t\t\t\t\tonMouseLeave={onCellHover ? () => fireHover(null) : undefined}\n\t\t\t\t\t\t\tonFocus={onCellHover ? () => fireHover(cellPayload) : undefined}\n\t\t\t\t\t\t\tonBlur={onCellHover ? () => fireHover(null) : undefined}\n\t\t\t\t\t\t\tonClick={onCellClick ? handleActivate : undefined}\n\t\t\t\t\t\t\tonKeyDown={onCellClick ? (e) => activateOnKey(e, handleActivate) : undefined}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<rect\n\t\t\t\t\t\t\t\tx={c.x}\n\t\t\t\t\t\t\t\ty={c.y}\n\t\t\t\t\t\t\t\twidth={cellSize}\n\t\t\t\t\t\t\t\theight={cellSize}\n\t\t\t\t\t\t\t\t// Floor at 0.08 so empty cells are visible as grid lines;\n\t\t\t\t\t\t\t\t// ramp up to 0.95 for max-value cells. `--chart-1` carries\n\t\t\t\t\t\t\t\t// the hue, opacity carries the magnitude. Falls back to\n\t\t\t\t\t\t\t\t// `--primary` for consumers whose theme presets predate\n\t\t\t\t\t\t\t\t// the chart token family.\n\t\t\t\t\t\t\t\tfill=\"hsl(var(--chart-1, var(--primary)))\"\n\t\t\t\t\t\t\t\tfillOpacity={0.08 + 0.87 * c.intensity}\n\t\t\t\t\t\t\t\tstroke=\"hsl(var(--background))\"\n\t\t\t\t\t\t\t\tstrokeWidth={0.5}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t{showValues && cellSize > MIN_CELL_SIZE_FOR_VALUE && c.value !== 0 ? (\n\t\t\t\t\t\t\t\t<text\n\t\t\t\t\t\t\t\t\tx={c.x + cellSize / 2}\n\t\t\t\t\t\t\t\t\ty={c.y + cellSize / 2}\n\t\t\t\t\t\t\t\t\tdy=\"0.35em\"\n\t\t\t\t\t\t\t\t\ttextAnchor=\"middle\"\n\t\t\t\t\t\t\t\t\tfontSize={9}\n\t\t\t\t\t\t\t\t\t// Above ~0.55 intensity the cell is dark enough that\n\t\t\t\t\t\t\t\t\t// background-tone foreground reads better.\n\t\t\t\t\t\t\t\t\tfill={c.intensity > 0.55 ? \"hsl(var(--background))\" : \"hsl(var(--foreground))\"}\n\t\t\t\t\t\t\t\t\tstyle={{ pointerEvents: \"none\" }}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{c.value}\n\t\t\t\t\t\t\t\t</text>\n\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t</g>\n\t\t\t\t\t);\n\t\t\t\t})}\n\t\t\t</g>\n\t\t</svg>\n\t);\n}\n\nfunction layout(\n\tnodes: MatrixNode[],\n\tmatrix: number[][],\n\tsize: number,\n\tlabelMargin: number,\n): LaidOutCell[] {\n\tif (nodes.length === 0) return [];\n\tconst cellSize = (size - labelMargin) / nodes.length;\n\tlet maxValue = 0;\n\tfor (const row of matrix) {\n\t\tfor (const v of row) if (v > maxValue) maxValue = v;\n\t}\n\tconst cells: LaidOutCell[] = [];\n\tfor (let i = 0; i < nodes.length; i++) {\n\t\tfor (let j = 0; j < nodes.length; j++) {\n\t\t\tconst value = matrix[i]?.[j] ?? 0;\n\t\t\tcells.push({\n\t\t\t\trow: nodes[i],\n\t\t\t\tcol: nodes[j],\n\t\t\t\trowIndex: i,\n\t\t\t\tcolIndex: j,\n\t\t\t\tvalue,\n\t\t\t\tx: labelMargin + cellSize * j,\n\t\t\t\ty: labelMargin + cellSize * i,\n\t\t\t\tintensity: maxValue > 0 ? value / maxValue : 0,\n\t\t\t});\n\t\t}\n\t}\n\treturn cells;\n}\n\nfunction activateOnKey(e: React.KeyboardEvent, fn: () => void): void {\n\tif (e.key === \"Enter\" || e.key === \" \") {\n\t\te.preventDefault();\n\t\tfn();\n\t}\n}\n\nexport { Matrix };\n"]}
|
package/dist/mind-map.js
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { clsx } from 'clsx';
|
|
4
|
+
import { twMerge } from 'tailwind-merge';
|
|
5
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
6
|
+
|
|
7
|
+
function cn(...inputs) {
|
|
8
|
+
return twMerge(clsx(inputs));
|
|
9
|
+
}
|
|
10
|
+
function MindMap({
|
|
11
|
+
root,
|
|
12
|
+
orientation = "radial",
|
|
13
|
+
width = 600,
|
|
14
|
+
height = 400,
|
|
15
|
+
onNodeClick,
|
|
16
|
+
className,
|
|
17
|
+
...rest
|
|
18
|
+
}) {
|
|
19
|
+
const [d3h, setD3h] = React.useState(null);
|
|
20
|
+
React.useEffect(() => {
|
|
21
|
+
let cancelled = false;
|
|
22
|
+
void import('d3-hierarchy').then((mod) => {
|
|
23
|
+
if (!cancelled) setD3h(mod);
|
|
24
|
+
});
|
|
25
|
+
return () => {
|
|
26
|
+
cancelled = true;
|
|
27
|
+
};
|
|
28
|
+
}, []);
|
|
29
|
+
if (!d3h) {
|
|
30
|
+
return /* @__PURE__ */ jsx(
|
|
31
|
+
"div",
|
|
32
|
+
{
|
|
33
|
+
"data-hex-mind-map-loading": true,
|
|
34
|
+
"aria-busy": "true",
|
|
35
|
+
className: cn("inline-block bg-muted/20", className),
|
|
36
|
+
style: { width, height }
|
|
37
|
+
}
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
const { nodes, links, viewBox } = layout(d3h, root, orientation, width, height);
|
|
41
|
+
const nodeCount = nodes.length;
|
|
42
|
+
const desc = `Mind map with ${nodeCount} node${nodeCount === 1 ? "" : "s"}, rooted at "${root.label}"`;
|
|
43
|
+
return /* @__PURE__ */ jsxs(
|
|
44
|
+
"svg",
|
|
45
|
+
{
|
|
46
|
+
...rest,
|
|
47
|
+
"data-hex-mind-map": true,
|
|
48
|
+
"data-orientation": orientation,
|
|
49
|
+
role: "img",
|
|
50
|
+
viewBox,
|
|
51
|
+
width,
|
|
52
|
+
height,
|
|
53
|
+
className: cn("block", className),
|
|
54
|
+
children: [
|
|
55
|
+
/* @__PURE__ */ jsx("title", { children: "Mind map" }),
|
|
56
|
+
/* @__PURE__ */ jsx("desc", { children: desc }),
|
|
57
|
+
/* @__PURE__ */ jsx("g", { "data-hex-mind-map-links": true, children: links.map((l) => /* @__PURE__ */ jsx(
|
|
58
|
+
"path",
|
|
59
|
+
{
|
|
60
|
+
d: linkPath(l, orientation),
|
|
61
|
+
fill: "none",
|
|
62
|
+
stroke: "hsl(var(--muted-foreground))",
|
|
63
|
+
strokeOpacity: 0.7,
|
|
64
|
+
strokeWidth: 1.5
|
|
65
|
+
},
|
|
66
|
+
l.id
|
|
67
|
+
)) }),
|
|
68
|
+
/* @__PURE__ */ jsx("g", { "data-hex-mind-map-nodes": true, children: nodes.map((n) => /* @__PURE__ */ jsxs(
|
|
69
|
+
"g",
|
|
70
|
+
{
|
|
71
|
+
"data-hex-mind-map-node": true,
|
|
72
|
+
"data-depth": n.depth,
|
|
73
|
+
transform: `translate(${n.x},${n.y})`,
|
|
74
|
+
style: onNodeClick ? { cursor: "pointer" } : void 0,
|
|
75
|
+
onClick: onNodeClick ? () => onNodeClick(n.node) : void 0,
|
|
76
|
+
children: [
|
|
77
|
+
/* @__PURE__ */ jsx(
|
|
78
|
+
"circle",
|
|
79
|
+
{
|
|
80
|
+
r: 4,
|
|
81
|
+
fill: "hsl(var(--primary))",
|
|
82
|
+
stroke: "hsl(var(--background))",
|
|
83
|
+
strokeWidth: 2
|
|
84
|
+
}
|
|
85
|
+
),
|
|
86
|
+
/* @__PURE__ */ jsx(
|
|
87
|
+
"text",
|
|
88
|
+
{
|
|
89
|
+
x: 8,
|
|
90
|
+
y: 4,
|
|
91
|
+
fontSize: 12,
|
|
92
|
+
fill: "hsl(var(--foreground))",
|
|
93
|
+
style: { paintOrder: "stroke" },
|
|
94
|
+
stroke: "hsl(var(--background))",
|
|
95
|
+
strokeWidth: 3,
|
|
96
|
+
strokeLinejoin: "round",
|
|
97
|
+
children: n.node.label
|
|
98
|
+
}
|
|
99
|
+
)
|
|
100
|
+
]
|
|
101
|
+
},
|
|
102
|
+
n.node.id
|
|
103
|
+
)) })
|
|
104
|
+
]
|
|
105
|
+
}
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
function layout(d3h, root, orientation, width, height) {
|
|
109
|
+
const hierarchy = d3h.hierarchy(root);
|
|
110
|
+
if (orientation === "radial") {
|
|
111
|
+
const radius = Math.min(width, height) / 2 - 16;
|
|
112
|
+
const layoutRoot2 = d3h.tree().size([2 * Math.PI, radius])(hierarchy);
|
|
113
|
+
const nodes2 = [];
|
|
114
|
+
const links2 = [];
|
|
115
|
+
layoutRoot2.each((d) => {
|
|
116
|
+
const point = polarToCartesian(d.x, d.y);
|
|
117
|
+
nodes2.push({ node: d.data, x: point.x, y: point.y, depth: d.depth });
|
|
118
|
+
});
|
|
119
|
+
layoutRoot2.links().forEach((link, i) => {
|
|
120
|
+
const sPt = polarToCartesian(link.source.x, link.source.y);
|
|
121
|
+
const tPt = polarToCartesian(link.target.x, link.target.y);
|
|
122
|
+
links2.push({ source: sPt, target: tPt, id: `l-${i}` });
|
|
123
|
+
});
|
|
124
|
+
const half = Math.min(width, height) / 2;
|
|
125
|
+
return {
|
|
126
|
+
nodes: nodes2,
|
|
127
|
+
links: links2,
|
|
128
|
+
viewBox: `${-half} ${-half} ${2 * half} ${2 * half}`
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
const layoutRoot = d3h.tree().size([height - 32, width - 200])(hierarchy);
|
|
132
|
+
const nodes = [];
|
|
133
|
+
const links = [];
|
|
134
|
+
layoutRoot.each((d) => {
|
|
135
|
+
nodes.push({ node: d.data, x: d.y, y: d.x, depth: d.depth });
|
|
136
|
+
});
|
|
137
|
+
layoutRoot.links().forEach((link, i) => {
|
|
138
|
+
links.push({
|
|
139
|
+
source: { x: link.source.y, y: link.source.x },
|
|
140
|
+
target: { x: link.target.y, y: link.target.x },
|
|
141
|
+
id: `l-${i}`
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
return {
|
|
145
|
+
nodes,
|
|
146
|
+
links,
|
|
147
|
+
viewBox: `0 0 ${width} ${height}`
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
function polarToCartesian(angle, radius) {
|
|
151
|
+
return {
|
|
152
|
+
x: radius * Math.cos(angle - Math.PI / 2),
|
|
153
|
+
y: radius * Math.sin(angle - Math.PI / 2)
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
function linkPath(link, orientation) {
|
|
157
|
+
const { source: s, target: t } = link;
|
|
158
|
+
if (orientation === "horizontal") {
|
|
159
|
+
const mx = (s.x + t.x) / 2;
|
|
160
|
+
return `M${s.x},${s.y} C${mx},${s.y} ${mx},${t.y} ${t.x},${t.y}`;
|
|
161
|
+
}
|
|
162
|
+
return `M${s.x},${s.y} C${s.x},${(s.y + t.y) / 2} ${t.x},${(s.y + t.y) / 2} ${t.x},${t.y}`;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export { MindMap };
|
|
166
|
+
//# sourceMappingURL=mind-map.js.map
|
|
167
|
+
//# sourceMappingURL=mind-map.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/utils.ts","../src/artifacts/mind-map/mind-map.tsx"],"names":["layoutRoot","nodes","links"],"mappings":"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","file":"mind-map.js","sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Merge class names with Tailwind CSS conflict resolution.\n * @param inputs - Class values (strings, arrays, objects) to merge\n * @returns A single merged class string with Tailwind conflicts resolved\n */\nexport function cn(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\n/**\n * Typed React MindMap. Pass a hierarchical `root` node and the component lays\n * the children out radially (or horizontally) using d3-hierarchy's tree\n * layout. No Mermaid string parsing — the data shape IS the API, so consumers\n * can build mind maps from typed application state instead of templating a\n * DSL.\n *\n * Heavy peer: requires `d3-hierarchy` (~3 KB gzip). The hex-core CLI's `add`\n * flow prompts before installing.\n *\n * @example\n * <MindMap\n * root={{\n * id: \"root\",\n * label: \"Project\",\n * children: [\n * { id: \"ui\", label: \"UI\", children: [{ id: \"btn\", label: \"Button\" }] },\n * { id: \"api\", label: \"API\" },\n * ],\n * }}\n * />\n */\nexport type MindMapNode = {\n\tid: string;\n\tlabel: string;\n\tchildren?: MindMapNode[];\n\tdata?: unknown;\n};\n\nexport interface MindMapProps extends Omit<React.SVGAttributes<SVGSVGElement>, \"children\"> {\n\t/** Root of the hierarchy. */\n\troot: MindMapNode;\n\t/** \"radial\" lays children around the root; \"horizontal\" runs left→right. Default \"radial\". */\n\torientation?: \"radial\" | \"horizontal\";\n\t/** Pixel width of the rendered SVG. Default 600. */\n\twidth?: number;\n\t/** Pixel height of the rendered SVG. Default 400. */\n\theight?: number;\n\t/** Fired when a node is clicked. */\n\tonNodeClick?: (node: MindMapNode) => void;\n}\n\ninterface LaidOutNode {\n\tnode: MindMapNode;\n\tx: number;\n\ty: number;\n\tdepth: number;\n}\n\ninterface LaidOutLink {\n\tsource: { x: number; y: number };\n\ttarget: { x: number; y: number };\n\tid: string;\n}\n\ntype D3HierarchyMod = typeof import(\"d3-hierarchy\");\n\nfunction MindMap({\n\troot,\n\torientation = \"radial\",\n\twidth = 600,\n\theight = 400,\n\tonNodeClick,\n\tclassName,\n\t...rest\n}: MindMapProps) {\n\tconst [d3h, setD3h] = React.useState<D3HierarchyMod | null>(null);\n\n\tReact.useEffect(() => {\n\t\tlet cancelled = false;\n\t\tvoid import(\"d3-hierarchy\").then((mod) => {\n\t\t\tif (!cancelled) setD3h(mod);\n\t\t});\n\t\treturn () => {\n\t\t\tcancelled = true;\n\t\t};\n\t}, []);\n\n\tif (!d3h) {\n\t\treturn (\n\t\t\t<div\n\t\t\t\tdata-hex-mind-map-loading\n\t\t\t\taria-busy=\"true\"\n\t\t\t\tclassName={cn(\"inline-block bg-muted/20\", className)}\n\t\t\t\tstyle={{ width, height }}\n\t\t\t/>\n\t\t);\n\t}\n\n\tconst { nodes, links, viewBox } = layout(d3h, root, orientation, width, height);\n\tconst nodeCount = nodes.length;\n\tconst desc = `Mind map with ${nodeCount} node${nodeCount === 1 ? \"\" : \"s\"}, rooted at \"${root.label}\"`;\n\n\treturn (\n\t\t<svg\n\t\t\t{...rest}\n\t\t\tdata-hex-mind-map\n\t\t\tdata-orientation={orientation}\n\t\t\trole=\"img\"\n\t\t\tviewBox={viewBox}\n\t\t\twidth={width}\n\t\t\theight={height}\n\t\t\tclassName={cn(\"block\", className)}\n\t\t>\n\t\t\t<title>Mind map</title>\n\t\t\t<desc>{desc}</desc>\n\t\t\t<g data-hex-mind-map-links>\n\t\t\t\t{links.map((l) => (\n\t\t\t\t\t<path\n\t\t\t\t\t\tkey={l.id}\n\t\t\t\t\t\td={linkPath(l, orientation)}\n\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\tstroke=\"hsl(var(--muted-foreground))\"\n\t\t\t\t\t\tstrokeOpacity={0.7}\n\t\t\t\t\t\tstrokeWidth={1.5}\n\t\t\t\t\t/>\n\t\t\t\t))}\n\t\t\t</g>\n\t\t\t<g data-hex-mind-map-nodes>\n\t\t\t\t{nodes.map((n) => (\n\t\t\t\t\t<g\n\t\t\t\t\t\tkey={n.node.id}\n\t\t\t\t\t\tdata-hex-mind-map-node\n\t\t\t\t\t\tdata-depth={n.depth}\n\t\t\t\t\t\ttransform={`translate(${n.x},${n.y})`}\n\t\t\t\t\t\tstyle={onNodeClick ? { cursor: \"pointer\" } : undefined}\n\t\t\t\t\t\tonClick={onNodeClick ? () => onNodeClick(n.node) : undefined}\n\t\t\t\t\t>\n\t\t\t\t\t\t<circle\n\t\t\t\t\t\t\tr={4}\n\t\t\t\t\t\t\tfill=\"hsl(var(--primary))\"\n\t\t\t\t\t\t\tstroke=\"hsl(var(--background))\"\n\t\t\t\t\t\t\tstrokeWidth={2}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<text\n\t\t\t\t\t\t\tx={8}\n\t\t\t\t\t\t\ty={4}\n\t\t\t\t\t\t\tfontSize={12}\n\t\t\t\t\t\t\tfill=\"hsl(var(--foreground))\"\n\t\t\t\t\t\t\tstyle={{ paintOrder: \"stroke\" }}\n\t\t\t\t\t\t\tstroke=\"hsl(var(--background))\"\n\t\t\t\t\t\t\tstrokeWidth={3}\n\t\t\t\t\t\t\tstrokeLinejoin=\"round\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{n.node.label}\n\t\t\t\t\t\t</text>\n\t\t\t\t\t</g>\n\t\t\t\t))}\n\t\t\t</g>\n\t\t</svg>\n\t);\n}\n\nfunction layout(\n\td3h: D3HierarchyMod,\n\troot: MindMapNode,\n\torientation: \"radial\" | \"horizontal\",\n\twidth: number,\n\theight: number,\n): { nodes: LaidOutNode[]; links: LaidOutLink[]; viewBox: string } {\n\tconst hierarchy = d3h.hierarchy<MindMapNode>(root);\n\n\tif (orientation === \"radial\") {\n\t\tconst radius = Math.min(width, height) / 2 - 16;\n\t\t// d3.tree(hierarchy) returns the layout-mutated root typed as\n\t\t// HierarchyPointNode<T> — its `x`/`y` are populated by the call.\n\t\tconst layoutRoot = d3h.tree<MindMapNode>().size([2 * Math.PI, radius])(hierarchy);\n\t\tconst nodes: LaidOutNode[] = [];\n\t\tconst links: LaidOutLink[] = [];\n\t\tlayoutRoot.each((d) => {\n\t\t\tconst point = polarToCartesian(d.x, d.y);\n\t\t\tnodes.push({ node: d.data, x: point.x, y: point.y, depth: d.depth });\n\t\t});\n\t\tlayoutRoot.links().forEach((link, i) => {\n\t\t\tconst sPt = polarToCartesian(link.source.x, link.source.y);\n\t\t\tconst tPt = polarToCartesian(link.target.x, link.target.y);\n\t\t\tlinks.push({ source: sPt, target: tPt, id: `l-${i}` });\n\t\t});\n\t\tconst half = Math.min(width, height) / 2;\n\t\treturn {\n\t\t\tnodes,\n\t\t\tlinks,\n\t\t\tviewBox: `${-half} ${-half} ${2 * half} ${2 * half}`,\n\t\t};\n\t}\n\n\tconst layoutRoot = d3h.tree<MindMapNode>().size([height - 32, width - 200])(hierarchy);\n\tconst nodes: LaidOutNode[] = [];\n\tconst links: LaidOutLink[] = [];\n\tlayoutRoot.each((d) => {\n\t\t// Horizontal: swap d3's (x, y) so depth runs along the SVG x-axis.\n\t\tnodes.push({ node: d.data, x: d.y, y: d.x, depth: d.depth });\n\t});\n\tlayoutRoot.links().forEach((link, i) => {\n\t\tlinks.push({\n\t\t\tsource: { x: link.source.y, y: link.source.x },\n\t\t\ttarget: { x: link.target.y, y: link.target.x },\n\t\t\tid: `l-${i}`,\n\t\t});\n\t});\n\treturn {\n\t\tnodes,\n\t\tlinks,\n\t\tviewBox: `0 0 ${width} ${height}`,\n\t};\n}\n\nfunction polarToCartesian(angle: number, radius: number): { x: number; y: number } {\n\treturn {\n\t\tx: radius * Math.cos(angle - Math.PI / 2),\n\t\ty: radius * Math.sin(angle - Math.PI / 2),\n\t};\n}\n\nfunction linkPath(link: LaidOutLink, orientation: \"radial\" | \"horizontal\"): string {\n\tconst { source: s, target: t } = link;\n\tif (orientation === \"horizontal\") {\n\t\tconst mx = (s.x + t.x) / 2;\n\t\treturn `M${s.x},${s.y} C${mx},${s.y} ${mx},${t.y} ${t.x},${t.y}`;\n\t}\n\treturn `M${s.x},${s.y} C${s.x},${(s.y + t.y) / 2} ${t.x},${(s.y + t.y) / 2} ${t.x},${t.y}`;\n}\n\nexport { MindMap };\n"]}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { clsx } from 'clsx';
|
|
4
|
+
import { twMerge } from 'tailwind-merge';
|
|
5
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
6
|
+
|
|
7
|
+
function cn(...inputs) {
|
|
8
|
+
return twMerge(clsx(inputs));
|
|
9
|
+
}
|
|
10
|
+
function OrgChart({
|
|
11
|
+
root,
|
|
12
|
+
collapsible = true,
|
|
13
|
+
defaultExpandedDepth = Number.POSITIVE_INFINITY,
|
|
14
|
+
nodeWidth = 180,
|
|
15
|
+
nodeHeight = 64,
|
|
16
|
+
width = 800,
|
|
17
|
+
height = 480,
|
|
18
|
+
onNodeClick,
|
|
19
|
+
className,
|
|
20
|
+
...rest
|
|
21
|
+
}) {
|
|
22
|
+
const [d3h, setD3h] = React.useState(null);
|
|
23
|
+
const [collapsed, setCollapsed] = React.useState(() => seedCollapsed(root, defaultExpandedDepth));
|
|
24
|
+
React.useEffect(() => {
|
|
25
|
+
let cancelled = false;
|
|
26
|
+
void import('d3-hierarchy').then((mod) => {
|
|
27
|
+
if (!cancelled) setD3h(mod);
|
|
28
|
+
});
|
|
29
|
+
return () => {
|
|
30
|
+
cancelled = true;
|
|
31
|
+
};
|
|
32
|
+
}, []);
|
|
33
|
+
React.useEffect(() => {
|
|
34
|
+
setCollapsed(seedCollapsed(root, defaultExpandedDepth));
|
|
35
|
+
}, [root, defaultExpandedDepth]);
|
|
36
|
+
if (!d3h) {
|
|
37
|
+
return /* @__PURE__ */ jsx(
|
|
38
|
+
"div",
|
|
39
|
+
{
|
|
40
|
+
"data-hex-org-chart-loading": true,
|
|
41
|
+
"aria-busy": "true",
|
|
42
|
+
className: cn("inline-block bg-muted/20", className),
|
|
43
|
+
style: { width, height }
|
|
44
|
+
}
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
const { nodes, links } = layout(d3h, root, collapsed, width, height, nodeHeight);
|
|
48
|
+
const desc = `Organizational chart with ${nodes.length} visible node${nodes.length === 1 ? "" : "s"}, rooted at "${root.label}"`;
|
|
49
|
+
const handleClick = (node) => {
|
|
50
|
+
onNodeClick?.(node);
|
|
51
|
+
if (!collapsible) return;
|
|
52
|
+
const hasChildren = node.children && node.children.length > 0;
|
|
53
|
+
if (!hasChildren) return;
|
|
54
|
+
setCollapsed((prev) => {
|
|
55
|
+
const next = new Set(prev);
|
|
56
|
+
if (next.has(node.id)) next.delete(node.id);
|
|
57
|
+
else next.add(node.id);
|
|
58
|
+
return next;
|
|
59
|
+
});
|
|
60
|
+
};
|
|
61
|
+
return /* @__PURE__ */ jsxs(
|
|
62
|
+
"svg",
|
|
63
|
+
{
|
|
64
|
+
...rest,
|
|
65
|
+
"data-hex-org-chart": true,
|
|
66
|
+
role: "img",
|
|
67
|
+
width,
|
|
68
|
+
height,
|
|
69
|
+
viewBox: `0 0 ${width} ${height}`,
|
|
70
|
+
className: cn("block", className),
|
|
71
|
+
children: [
|
|
72
|
+
/* @__PURE__ */ jsx("title", { children: "Org chart" }),
|
|
73
|
+
/* @__PURE__ */ jsx("desc", { children: desc }),
|
|
74
|
+
/* @__PURE__ */ jsx("g", { "data-hex-org-chart-links": true, children: links.map((l) => /* @__PURE__ */ jsx(
|
|
75
|
+
"path",
|
|
76
|
+
{
|
|
77
|
+
d: `M${l.source.x},${l.source.y} C${l.source.x},${(l.source.y + l.target.y) / 2} ${l.target.x},${(l.source.y + l.target.y) / 2} ${l.target.x},${l.target.y}`,
|
|
78
|
+
fill: "none",
|
|
79
|
+
stroke: "hsl(var(--muted-foreground))",
|
|
80
|
+
strokeOpacity: 0.5,
|
|
81
|
+
strokeWidth: 1
|
|
82
|
+
},
|
|
83
|
+
l.id
|
|
84
|
+
)) }),
|
|
85
|
+
/* @__PURE__ */ jsx("g", { "data-hex-org-chart-nodes": true, children: nodes.map((n) => /* @__PURE__ */ jsxs(
|
|
86
|
+
"g",
|
|
87
|
+
{
|
|
88
|
+
"data-hex-org-chart-node": true,
|
|
89
|
+
"data-depth": n.depth,
|
|
90
|
+
transform: `translate(${n.x - nodeWidth / 2},${n.y - nodeHeight / 2})`,
|
|
91
|
+
style: collapsible || onNodeClick ? { cursor: "pointer" } : void 0,
|
|
92
|
+
onClick: collapsible || onNodeClick ? () => handleClick(n.node) : void 0,
|
|
93
|
+
children: [
|
|
94
|
+
/* @__PURE__ */ jsx(
|
|
95
|
+
"rect",
|
|
96
|
+
{
|
|
97
|
+
width: nodeWidth,
|
|
98
|
+
height: nodeHeight,
|
|
99
|
+
rx: 8,
|
|
100
|
+
ry: 8,
|
|
101
|
+
fill: "hsl(var(--card))",
|
|
102
|
+
stroke: "hsl(var(--border))",
|
|
103
|
+
strokeWidth: 1
|
|
104
|
+
}
|
|
105
|
+
),
|
|
106
|
+
/* @__PURE__ */ jsx(
|
|
107
|
+
"text",
|
|
108
|
+
{
|
|
109
|
+
x: 12,
|
|
110
|
+
y: 24,
|
|
111
|
+
fontSize: 13,
|
|
112
|
+
fontWeight: 600,
|
|
113
|
+
fill: "hsl(var(--foreground))",
|
|
114
|
+
children: truncate(n.node.label, Math.floor((nodeWidth - 24) / 7))
|
|
115
|
+
}
|
|
116
|
+
),
|
|
117
|
+
n.node.subtitle ? /* @__PURE__ */ jsx(
|
|
118
|
+
"text",
|
|
119
|
+
{
|
|
120
|
+
x: 12,
|
|
121
|
+
y: 42,
|
|
122
|
+
fontSize: 11,
|
|
123
|
+
fill: "hsl(var(--muted-foreground))",
|
|
124
|
+
children: truncate(n.node.subtitle, Math.floor((nodeWidth - 24) / 6))
|
|
125
|
+
}
|
|
126
|
+
) : null,
|
|
127
|
+
n.collapsedCount > 0 ? /* @__PURE__ */ jsxs("g", { transform: `translate(${nodeWidth - 28},${nodeHeight - 18})`, children: [
|
|
128
|
+
/* @__PURE__ */ jsx(
|
|
129
|
+
"rect",
|
|
130
|
+
{
|
|
131
|
+
width: 20,
|
|
132
|
+
height: 14,
|
|
133
|
+
rx: 7,
|
|
134
|
+
ry: 7,
|
|
135
|
+
fill: "hsl(var(--primary))",
|
|
136
|
+
opacity: 0.85
|
|
137
|
+
}
|
|
138
|
+
),
|
|
139
|
+
/* @__PURE__ */ jsx(
|
|
140
|
+
"text",
|
|
141
|
+
{
|
|
142
|
+
x: 10,
|
|
143
|
+
y: 10,
|
|
144
|
+
fontSize: 9,
|
|
145
|
+
fontWeight: 600,
|
|
146
|
+
fill: "hsl(var(--primary-foreground))",
|
|
147
|
+
textAnchor: "middle",
|
|
148
|
+
children: `+${n.collapsedCount}`
|
|
149
|
+
}
|
|
150
|
+
)
|
|
151
|
+
] }) : null
|
|
152
|
+
]
|
|
153
|
+
},
|
|
154
|
+
n.node.id
|
|
155
|
+
)) })
|
|
156
|
+
]
|
|
157
|
+
}
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
function seedCollapsed(root, maxDepth) {
|
|
161
|
+
if (!Number.isFinite(maxDepth)) return /* @__PURE__ */ new Set();
|
|
162
|
+
const collapsed = /* @__PURE__ */ new Set();
|
|
163
|
+
const walk = (n, depth) => {
|
|
164
|
+
if (depth >= maxDepth && n.children && n.children.length > 0) {
|
|
165
|
+
collapsed.add(n.id);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
n.children?.forEach((c) => walk(c, depth + 1));
|
|
169
|
+
};
|
|
170
|
+
walk(root, 0);
|
|
171
|
+
return collapsed;
|
|
172
|
+
}
|
|
173
|
+
function layout(d3h, root, collapsed, width, height, nodeHeight) {
|
|
174
|
+
const indexById = /* @__PURE__ */ new Map();
|
|
175
|
+
indexNodes(root, indexById);
|
|
176
|
+
const visible = pruneCollapsed(root, collapsed);
|
|
177
|
+
const hierarchy = d3h.hierarchy(visible);
|
|
178
|
+
const layoutRoot = d3h.tree().size([width - 32, height - nodeHeight])(hierarchy);
|
|
179
|
+
const nodes = [];
|
|
180
|
+
layoutRoot.each((d) => {
|
|
181
|
+
const original = indexById.get(d.data.id);
|
|
182
|
+
const collapsedCount = collapsed.has(d.data.id) && original ? countDescendants(original) : 0;
|
|
183
|
+
nodes.push({ node: d.data, x: d.x + 16, y: d.y + nodeHeight / 2, depth: d.depth, collapsedCount });
|
|
184
|
+
});
|
|
185
|
+
const links = layoutRoot.links().map((link, i) => ({
|
|
186
|
+
source: { x: link.source.x + 16, y: link.source.y + nodeHeight / 2 },
|
|
187
|
+
target: { x: link.target.x + 16, y: link.target.y + nodeHeight / 2 },
|
|
188
|
+
id: `l-${i}`
|
|
189
|
+
}));
|
|
190
|
+
return { nodes, links };
|
|
191
|
+
}
|
|
192
|
+
function pruneCollapsed(node, collapsed) {
|
|
193
|
+
if (collapsed.has(node.id)) {
|
|
194
|
+
return { ...node, children: void 0 };
|
|
195
|
+
}
|
|
196
|
+
if (!node.children || node.children.length === 0) return node;
|
|
197
|
+
return { ...node, children: node.children.map((c) => pruneCollapsed(c, collapsed)) };
|
|
198
|
+
}
|
|
199
|
+
function indexNodes(node, out) {
|
|
200
|
+
out.set(node.id, node);
|
|
201
|
+
if (!node.children) return;
|
|
202
|
+
for (const c of node.children) indexNodes(c, out);
|
|
203
|
+
}
|
|
204
|
+
function countDescendants(node) {
|
|
205
|
+
if (!node.children || node.children.length === 0) return 0;
|
|
206
|
+
return node.children.reduce((sum, c) => sum + 1 + countDescendants(c), 0);
|
|
207
|
+
}
|
|
208
|
+
function truncate(s, max) {
|
|
209
|
+
if (s.length <= max) return s;
|
|
210
|
+
return `${s.slice(0, Math.max(1, max - 1))}\u2026`;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export { OrgChart };
|
|
214
|
+
//# sourceMappingURL=org-chart.js.map
|
|
215
|
+
//# sourceMappingURL=org-chart.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/utils.ts","../src/artifacts/org-chart/org-chart.tsx"],"names":[],"mappings":"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","file":"org-chart.js","sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Merge class names with Tailwind CSS conflict resolution.\n * @param inputs - Class values (strings, arrays, objects) to merge\n * @returns A single merged class string with Tailwind conflicts resolved\n */\nexport function cn(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\n/**\n * Top-down organizational chart. Pass a hierarchy of people / teams / units\n * and the component lays them out vertically using d3-hierarchy's tree\n * layout. Nodes can be collapsed by clicking; collapsed branches contribute\n * a \"+N\" badge but no further layout.\n *\n * Heavy peer: requires `d3-hierarchy` (~3 KB gzip).\n *\n * @example\n * <OrgChart\n * root={{\n * id: \"ceo\",\n * label: \"Jane Doe\",\n * subtitle: \"CEO\",\n * children: [\n * { id: \"cto\", label: \"Bob Smith\", subtitle: \"CTO\" },\n * ],\n * }}\n * />\n */\nexport type OrgNode = {\n\tid: string;\n\tlabel: string;\n\tsubtitle?: string;\n\tavatarUrl?: string;\n\tchildren?: OrgNode[];\n};\n\nexport interface OrgChartProps extends Omit<React.SVGAttributes<SVGSVGElement>, \"children\"> {\n\t/** Root of the org hierarchy. */\n\troot: OrgNode;\n\t/** Allow nodes to be clicked to collapse their subtree. Default true. */\n\tcollapsible?: boolean;\n\t/** All nodes deeper than this depth render collapsed by default. Default Infinity (all expanded). */\n\tdefaultExpandedDepth?: number;\n\t/** Pixel width of each node card. Default 180. */\n\tnodeWidth?: number;\n\t/** Pixel height of each node card. Default 64. */\n\tnodeHeight?: number;\n\t/** Pixel width of the rendered SVG. Default 800. */\n\twidth?: number;\n\t/** Pixel height of the rendered SVG. Default 480. */\n\theight?: number;\n\t/** Fired when a node is clicked. Fires before any internal collapse toggle. */\n\tonNodeClick?: (node: OrgNode) => void;\n}\n\ninterface LaidOut {\n\tnode: OrgNode;\n\tx: number;\n\ty: number;\n\tdepth: number;\n\tcollapsedCount: number;\n}\n\ninterface LaidOutLink {\n\tsource: { x: number; y: number };\n\ttarget: { x: number; y: number };\n\tid: string;\n}\n\ntype D3HierarchyMod = typeof import(\"d3-hierarchy\");\n\nfunction OrgChart({\n\troot,\n\tcollapsible = true,\n\tdefaultExpandedDepth = Number.POSITIVE_INFINITY,\n\tnodeWidth = 180,\n\tnodeHeight = 64,\n\twidth = 800,\n\theight = 480,\n\tonNodeClick,\n\tclassName,\n\t...rest\n}: OrgChartProps) {\n\tconst [d3h, setD3h] = React.useState<D3HierarchyMod | null>(null);\n\tconst [collapsed, setCollapsed] = React.useState<Set<string>>(() => seedCollapsed(root, defaultExpandedDepth));\n\n\tReact.useEffect(() => {\n\t\tlet cancelled = false;\n\t\tvoid import(\"d3-hierarchy\").then((mod) => {\n\t\t\tif (!cancelled) setD3h(mod);\n\t\t});\n\t\treturn () => {\n\t\t\tcancelled = true;\n\t\t};\n\t}, []);\n\n\t// Reset collapsed state when the root reference changes — otherwise stale\n\t// ids accumulate across data swaps (memory leak + phantom collapses if ids\n\t// happen to be reused). Mirrors Sunburst's focusId reset.\n\tReact.useEffect(() => {\n\t\tsetCollapsed(seedCollapsed(root, defaultExpandedDepth));\n\t}, [root, defaultExpandedDepth]);\n\n\tif (!d3h) {\n\t\treturn (\n\t\t\t<div\n\t\t\t\tdata-hex-org-chart-loading\n\t\t\t\taria-busy=\"true\"\n\t\t\t\tclassName={cn(\"inline-block bg-muted/20\", className)}\n\t\t\t\tstyle={{ width, height }}\n\t\t\t/>\n\t\t);\n\t}\n\n\tconst { nodes, links } = layout(d3h, root, collapsed, width, height, nodeHeight);\n\tconst desc = `Organizational chart with ${nodes.length} visible node${nodes.length === 1 ? \"\" : \"s\"}, rooted at \"${root.label}\"`;\n\n\tconst handleClick = (node: OrgNode) => {\n\t\tonNodeClick?.(node);\n\t\tif (!collapsible) return;\n\t\tconst hasChildren = node.children && node.children.length > 0;\n\t\tif (!hasChildren) return;\n\t\tsetCollapsed((prev) => {\n\t\t\tconst next = new Set(prev);\n\t\t\tif (next.has(node.id)) next.delete(node.id);\n\t\t\telse next.add(node.id);\n\t\t\treturn next;\n\t\t});\n\t};\n\n\treturn (\n\t\t<svg\n\t\t\t{...rest}\n\t\t\tdata-hex-org-chart\n\t\t\trole=\"img\"\n\t\t\twidth={width}\n\t\t\theight={height}\n\t\t\tviewBox={`0 0 ${width} ${height}`}\n\t\t\tclassName={cn(\"block\", className)}\n\t\t>\n\t\t\t<title>Org chart</title>\n\t\t\t<desc>{desc}</desc>\n\t\t\t<g data-hex-org-chart-links>\n\t\t\t\t{links.map((l) => (\n\t\t\t\t\t<path\n\t\t\t\t\t\tkey={l.id}\n\t\t\t\t\t\td={`M${l.source.x},${l.source.y} C${l.source.x},${(l.source.y + l.target.y) / 2} ${l.target.x},${(l.source.y + l.target.y) / 2} ${l.target.x},${l.target.y}`}\n\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\tstroke=\"hsl(var(--muted-foreground))\"\n\t\t\t\t\t\tstrokeOpacity={0.5}\n\t\t\t\t\t\tstrokeWidth={1}\n\t\t\t\t\t/>\n\t\t\t\t))}\n\t\t\t</g>\n\t\t\t<g data-hex-org-chart-nodes>\n\t\t\t\t{nodes.map((n) => (\n\t\t\t\t\t<g\n\t\t\t\t\t\tkey={n.node.id}\n\t\t\t\t\t\tdata-hex-org-chart-node\n\t\t\t\t\t\tdata-depth={n.depth}\n\t\t\t\t\t\ttransform={`translate(${n.x - nodeWidth / 2},${n.y - nodeHeight / 2})`}\n\t\t\t\t\t\tstyle={collapsible || onNodeClick ? { cursor: \"pointer\" } : undefined}\n\t\t\t\t\t\tonClick={collapsible || onNodeClick ? () => handleClick(n.node) : undefined}\n\t\t\t\t\t>\n\t\t\t\t\t\t<rect\n\t\t\t\t\t\t\twidth={nodeWidth}\n\t\t\t\t\t\t\theight={nodeHeight}\n\t\t\t\t\t\t\trx={8}\n\t\t\t\t\t\t\try={8}\n\t\t\t\t\t\t\tfill=\"hsl(var(--card))\"\n\t\t\t\t\t\t\tstroke=\"hsl(var(--border))\"\n\t\t\t\t\t\t\tstrokeWidth={1}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<text\n\t\t\t\t\t\t\tx={12}\n\t\t\t\t\t\t\ty={24}\n\t\t\t\t\t\t\tfontSize={13}\n\t\t\t\t\t\t\tfontWeight={600}\n\t\t\t\t\t\t\tfill=\"hsl(var(--foreground))\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{truncate(n.node.label, Math.floor((nodeWidth - 24) / 7))}\n\t\t\t\t\t\t</text>\n\t\t\t\t\t\t{n.node.subtitle ? (\n\t\t\t\t\t\t\t<text\n\t\t\t\t\t\t\t\tx={12}\n\t\t\t\t\t\t\t\ty={42}\n\t\t\t\t\t\t\t\tfontSize={11}\n\t\t\t\t\t\t\t\tfill=\"hsl(var(--muted-foreground))\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{truncate(n.node.subtitle, Math.floor((nodeWidth - 24) / 6))}\n\t\t\t\t\t\t\t</text>\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t{n.collapsedCount > 0 ? (\n\t\t\t\t\t\t\t<g transform={`translate(${nodeWidth - 28},${nodeHeight - 18})`}>\n\t\t\t\t\t\t\t\t<rect\n\t\t\t\t\t\t\t\t\twidth={20}\n\t\t\t\t\t\t\t\t\theight={14}\n\t\t\t\t\t\t\t\t\trx={7}\n\t\t\t\t\t\t\t\t\try={7}\n\t\t\t\t\t\t\t\t\tfill=\"hsl(var(--primary))\"\n\t\t\t\t\t\t\t\t\topacity={0.85}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t<text\n\t\t\t\t\t\t\t\t\tx={10}\n\t\t\t\t\t\t\t\t\ty={10}\n\t\t\t\t\t\t\t\t\tfontSize={9}\n\t\t\t\t\t\t\t\t\tfontWeight={600}\n\t\t\t\t\t\t\t\t\tfill=\"hsl(var(--primary-foreground))\"\n\t\t\t\t\t\t\t\t\ttextAnchor=\"middle\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{`+${n.collapsedCount}`}\n\t\t\t\t\t\t\t\t</text>\n\t\t\t\t\t\t\t</g>\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t</g>\n\t\t\t\t))}\n\t\t\t</g>\n\t\t</svg>\n\t);\n}\n\nfunction seedCollapsed(root: OrgNode, maxDepth: number): Set<string> {\n\tif (!Number.isFinite(maxDepth)) return new Set();\n\tconst collapsed = new Set<string>();\n\tconst walk = (n: OrgNode, depth: number) => {\n\t\tif (depth >= maxDepth && n.children && n.children.length > 0) {\n\t\t\tcollapsed.add(n.id);\n\t\t\treturn;\n\t\t}\n\t\tn.children?.forEach((c) => walk(c, depth + 1));\n\t};\n\twalk(root, 0);\n\treturn collapsed;\n}\n\nfunction layout(\n\td3h: D3HierarchyMod,\n\troot: OrgNode,\n\tcollapsed: Set<string>,\n\twidth: number,\n\theight: number,\n\tnodeHeight: number,\n): { nodes: LaidOut[]; links: LaidOutLink[] } {\n\t// Pre-build an id → original-node map so collapsed-count lookup is O(1) per\n\t// node (vs O(N) DFS), keeping the whole layout pass O(N) for trees up to a\n\t// few thousand nodes.\n\tconst indexById = new Map<string, OrgNode>();\n\tindexNodes(root, indexById);\n\n\tconst visible = pruneCollapsed(root, collapsed);\n\tconst hierarchy = d3h.hierarchy<OrgNode>(visible);\n\tconst layoutRoot = d3h.tree<OrgNode>().size([width - 32, height - nodeHeight])(hierarchy);\n\tconst nodes: LaidOut[] = [];\n\tlayoutRoot.each((d) => {\n\t\tconst original = indexById.get(d.data.id);\n\t\tconst collapsedCount = collapsed.has(d.data.id) && original ? countDescendants(original) : 0;\n\t\tnodes.push({ node: d.data, x: d.x + 16, y: d.y + nodeHeight / 2, depth: d.depth, collapsedCount });\n\t});\n\tconst links: LaidOutLink[] = layoutRoot.links().map((link, i) => ({\n\t\tsource: { x: link.source.x + 16, y: link.source.y + nodeHeight / 2 },\n\t\ttarget: { x: link.target.x + 16, y: link.target.y + nodeHeight / 2 },\n\t\tid: `l-${i}`,\n\t}));\n\treturn { nodes, links };\n}\n\nfunction pruneCollapsed(node: OrgNode, collapsed: Set<string>): OrgNode {\n\tif (collapsed.has(node.id)) {\n\t\treturn { ...node, children: undefined };\n\t}\n\tif (!node.children || node.children.length === 0) return node;\n\treturn { ...node, children: node.children.map((c) => pruneCollapsed(c, collapsed)) };\n}\n\nfunction indexNodes(node: OrgNode, out: Map<string, OrgNode>): void {\n\tout.set(node.id, node);\n\tif (!node.children) return;\n\tfor (const c of node.children) indexNodes(c, out);\n}\n\nfunction countDescendants(node: OrgNode): number {\n\tif (!node.children || node.children.length === 0) return 0;\n\treturn node.children.reduce((sum, c) => sum + 1 + countDescendants(c), 0);\n}\n\nfunction truncate(s: string, max: number): string {\n\tif (s.length <= max) return s;\n\treturn `${s.slice(0, Math.max(1, max - 1))}…`;\n}\n\nexport { OrgChart };\n"]}
|