@dxos/plugin-explorer 0.8.4-main.fd6878d → 0.9.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.
Files changed (278) hide show
  1. package/LICENSE +102 -5
  2. package/PLUGIN.mdl +340 -0
  3. package/dist/lib/neutral/ExplorerArticle-4I7PNGDC.mjs +459 -0
  4. package/dist/lib/neutral/ExplorerArticle-4I7PNGDC.mjs.map +7 -0
  5. package/dist/lib/neutral/ExplorerPlugin.mjs +10 -0
  6. package/dist/lib/neutral/capabilities/index.mjs +11 -0
  7. package/dist/lib/neutral/capabilities/index.mjs.map +7 -0
  8. package/dist/lib/neutral/chunk-3D7BYXOR.mjs +37 -0
  9. package/dist/lib/neutral/chunk-3D7BYXOR.mjs.map +7 -0
  10. package/dist/lib/neutral/chunk-42BYLQQA.mjs +42 -0
  11. package/dist/lib/neutral/chunk-42BYLQQA.mjs.map +7 -0
  12. package/dist/lib/neutral/chunk-7XUDLV6E.mjs +287 -0
  13. package/dist/lib/neutral/chunk-7XUDLV6E.mjs.map +7 -0
  14. package/dist/lib/neutral/chunk-IKHJV3Q4.mjs +20 -0
  15. package/dist/lib/neutral/chunk-IKHJV3Q4.mjs.map +7 -0
  16. package/dist/lib/neutral/chunk-J5LGTIGS.mjs +10 -0
  17. package/dist/lib/neutral/chunk-YBCHBVCJ.mjs +69 -0
  18. package/dist/lib/neutral/chunk-YBCHBVCJ.mjs.map +7 -0
  19. package/dist/lib/{node-esm/chunk-W4ZNCGOD.mjs → neutral/components/index.mjs} +890 -314
  20. package/dist/lib/neutral/components/index.mjs.map +7 -0
  21. package/dist/lib/neutral/containers/index.mjs +9 -0
  22. package/dist/lib/neutral/containers/index.mjs.map +7 -0
  23. package/dist/lib/neutral/create-object-F6TKVAGV.mjs +39 -0
  24. package/dist/lib/neutral/create-object-F6TKVAGV.mjs.map +7 -0
  25. package/dist/lib/neutral/hooks/index.mjs +45 -0
  26. package/dist/lib/neutral/hooks/index.mjs.map +7 -0
  27. package/dist/lib/neutral/index.mjs +14 -0
  28. package/dist/lib/neutral/meta.json +1 -0
  29. package/dist/lib/{browser → neutral}/meta.mjs +2 -3
  30. package/dist/lib/neutral/plugin.mjs +12 -0
  31. package/dist/lib/neutral/plugin.mjs.map +7 -0
  32. package/dist/lib/neutral/react-surface-APBW2VQG.mjs +26 -0
  33. package/dist/lib/neutral/react-surface-APBW2VQG.mjs.map +7 -0
  34. package/dist/lib/neutral/testing/index.mjs +139 -0
  35. package/dist/lib/neutral/testing/index.mjs.map +7 -0
  36. package/dist/lib/neutral/translations.mjs +33 -0
  37. package/dist/lib/neutral/translations.mjs.map +7 -0
  38. package/dist/lib/neutral/types/index.mjs +10 -0
  39. package/dist/lib/neutral/types/index.mjs.map +7 -0
  40. package/dist/types/data/cities.d.ts +4 -4
  41. package/dist/types/data/cities.d.ts.map +1 -1
  42. package/dist/types/data/countries-110m.d.ts +19 -22
  43. package/dist/types/data/countries-110m.d.ts.map +1 -1
  44. package/dist/types/src/ExplorerPlugin.d.ts +3 -1
  45. package/dist/types/src/ExplorerPlugin.d.ts.map +1 -1
  46. package/dist/types/src/ExplorerPlugin.test.d.ts +2 -0
  47. package/dist/types/src/ExplorerPlugin.test.d.ts.map +1 -0
  48. package/dist/types/src/capabilities/create-object.d.ts +11 -0
  49. package/dist/types/src/capabilities/create-object.d.ts.map +1 -0
  50. package/dist/types/src/capabilities/index.d.ts +8 -2
  51. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  52. package/dist/types/src/capabilities/react-surface.d.ts +3 -2
  53. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  54. package/dist/types/src/components/Chart/Chart.d.ts +1 -1
  55. package/dist/types/src/components/Chart/Chart.d.ts.map +1 -1
  56. package/dist/types/src/components/Chart/Chart.stories.d.ts +12 -5
  57. package/dist/types/src/components/Chart/Chart.stories.d.ts.map +1 -1
  58. package/dist/types/src/components/Globe/Globe.d.ts +1 -1
  59. package/dist/types/src/components/Globe/Globe.d.ts.map +1 -1
  60. package/dist/types/src/components/Globe/Globe.stories.d.ts +12 -5
  61. package/dist/types/src/components/Globe/Globe.stories.d.ts.map +1 -1
  62. package/dist/types/src/components/Graph/CanvasForceGraph.d.ts +13 -0
  63. package/dist/types/src/components/Graph/CanvasForceGraph.d.ts.map +1 -0
  64. package/dist/types/src/components/Graph/CanvasForceGraph.stories.d.ts +17 -0
  65. package/dist/types/src/components/Graph/CanvasForceGraph.stories.d.ts.map +1 -0
  66. package/dist/types/src/components/Graph/ForceGraph.d.ts +12 -5
  67. package/dist/types/src/components/Graph/ForceGraph.d.ts.map +1 -1
  68. package/dist/types/src/components/Graph/ForceGraph.stories.d.ts +15 -4
  69. package/dist/types/src/components/Graph/ForceGraph.stories.d.ts.map +1 -1
  70. package/dist/types/src/components/Graph/{adapter.d.ts → graph-adapter.d.ts} +2 -2
  71. package/dist/types/src/components/Graph/graph-adapter.d.ts.map +1 -0
  72. package/dist/types/src/components/Graph/index.d.ts +1 -1
  73. package/dist/types/src/components/Graph/index.d.ts.map +1 -1
  74. package/dist/types/src/components/Lattice/Lattice.d.ts +20 -0
  75. package/dist/types/src/components/Lattice/Lattice.d.ts.map +1 -0
  76. package/dist/types/src/components/Lattice/Lattice.stories.d.ts +8 -0
  77. package/dist/types/src/components/Lattice/Lattice.stories.d.ts.map +1 -0
  78. package/dist/types/src/components/Lattice/index.d.ts +2 -0
  79. package/dist/types/src/components/Lattice/index.d.ts.map +1 -0
  80. package/dist/types/src/components/Tree/EdgeBundling.stories.d.ts +21 -0
  81. package/dist/types/src/components/Tree/EdgeBundling.stories.d.ts.map +1 -0
  82. package/dist/types/src/components/Tree/Tree.d.ts +20 -23
  83. package/dist/types/src/components/Tree/Tree.d.ts.map +1 -1
  84. package/dist/types/src/components/Tree/Tree.stories.d.ts +8 -18
  85. package/dist/types/src/components/Tree/Tree.stories.d.ts.map +1 -1
  86. package/dist/types/src/components/Tree/index.d.ts +2 -0
  87. package/dist/types/src/components/Tree/index.d.ts.map +1 -1
  88. package/dist/types/src/components/Tree/layout/HierarchicalEdgeBundling.d.ts +37 -2
  89. package/dist/types/src/components/Tree/layout/HierarchicalEdgeBundling.d.ts.map +1 -1
  90. package/dist/types/src/components/Tree/layout/RadialTree.d.ts +35 -2
  91. package/dist/types/src/components/Tree/layout/RadialTree.d.ts.map +1 -1
  92. package/dist/types/src/components/Tree/layout/TidyTree.d.ts +24 -2
  93. package/dist/types/src/components/Tree/layout/TidyTree.d.ts.map +1 -1
  94. package/dist/types/src/components/Tree/layout/hierarchy.d.ts +17 -0
  95. package/dist/types/src/components/Tree/layout/hierarchy.d.ts.map +1 -0
  96. package/dist/types/src/components/Tree/layout/index.d.ts +5 -4
  97. package/dist/types/src/components/Tree/layout/index.d.ts.map +1 -1
  98. package/dist/types/src/components/Tree/layout/slots.d.ts +7 -0
  99. package/dist/types/src/components/Tree/layout/slots.d.ts.map +1 -0
  100. package/dist/types/src/components/Tree/layout/useContainerSize.d.ts +15 -0
  101. package/dist/types/src/components/Tree/layout/useContainerSize.d.ts.map +1 -0
  102. package/dist/types/src/components/Tree/types/tree.d.ts +51 -28
  103. package/dist/types/src/components/Tree/types/tree.d.ts.map +1 -1
  104. package/dist/types/src/components/Tree/types/types.d.ts +14 -4
  105. package/dist/types/src/components/Tree/types/types.d.ts.map +1 -1
  106. package/dist/types/src/components/index.d.ts +1 -4
  107. package/dist/types/src/components/index.d.ts.map +1 -1
  108. package/dist/types/src/containers/ExplorerArticle/ExplorerArticle.d.ts +8 -0
  109. package/dist/types/src/containers/ExplorerArticle/ExplorerArticle.d.ts.map +1 -0
  110. package/dist/types/src/containers/ExplorerArticle/ExplorerArticle.stories.d.ts +15 -0
  111. package/dist/types/src/containers/ExplorerArticle/ExplorerArticle.stories.d.ts.map +1 -0
  112. package/dist/types/src/containers/ExplorerArticle/Visualization.d.ts +18 -0
  113. package/dist/types/src/containers/ExplorerArticle/Visualization.d.ts.map +1 -0
  114. package/dist/types/src/containers/ExplorerArticle/index.d.ts +2 -0
  115. package/dist/types/src/containers/ExplorerArticle/index.d.ts.map +1 -0
  116. package/dist/types/src/containers/ExplorerArticle/variants.d.ts +9 -0
  117. package/dist/types/src/containers/ExplorerArticle/variants.d.ts.map +1 -0
  118. package/dist/types/src/containers/index.d.ts +3 -0
  119. package/dist/types/src/containers/index.d.ts.map +1 -0
  120. package/dist/types/src/hooks/useGraphModel.d.ts +2 -2
  121. package/dist/types/src/hooks/useGraphModel.d.ts.map +1 -1
  122. package/dist/types/src/index.d.ts +1 -3
  123. package/dist/types/src/index.d.ts.map +1 -1
  124. package/dist/types/src/meta.d.ts +2 -3
  125. package/dist/types/src/meta.d.ts.map +1 -1
  126. package/dist/types/src/plugin.d.ts +3 -0
  127. package/dist/types/src/plugin.d.ts.map +1 -0
  128. package/dist/types/src/{components/Tree/testing → testing}/generator.d.ts +1 -1
  129. package/dist/types/src/testing/generator.d.ts.map +1 -0
  130. package/dist/types/src/testing/index.d.ts +4 -0
  131. package/dist/types/src/testing/index.d.ts.map +1 -0
  132. package/dist/types/src/testing/relations.d.ts +32 -0
  133. package/dist/types/src/testing/relations.d.ts.map +1 -0
  134. package/dist/types/src/translations.d.ts +34 -13
  135. package/dist/types/src/translations.d.ts.map +1 -1
  136. package/dist/types/src/types/ExplorerAction.d.ts +6 -0
  137. package/dist/types/src/types/ExplorerAction.d.ts.map +1 -0
  138. package/dist/types/src/types/Graph.d.ts +22 -0
  139. package/dist/types/src/types/Graph.d.ts.map +1 -0
  140. package/dist/types/src/types/index.d.ts +2 -2
  141. package/dist/types/src/types/index.d.ts.map +1 -1
  142. package/dist/types/src/util/index.d.ts +3 -0
  143. package/dist/types/src/util/index.d.ts.map +1 -0
  144. package/dist/types/src/util/node-color.d.ts +13 -0
  145. package/dist/types/src/util/node-color.d.ts.map +1 -0
  146. package/dist/types/src/{components → util}/plot.d.ts +1 -1
  147. package/dist/types/src/util/plot.d.ts.map +1 -0
  148. package/dist/types/tsconfig.tsbuildinfo +1 -1
  149. package/package.json +113 -64
  150. package/src/ExplorerPlugin.test.ts +26 -0
  151. package/src/ExplorerPlugin.tsx +21 -54
  152. package/src/capabilities/create-object.ts +36 -0
  153. package/src/capabilities/index.ts +3 -3
  154. package/src/capabilities/react-surface.tsx +24 -15
  155. package/src/components/Chart/Chart.stories.tsx +21 -27
  156. package/src/components/Chart/Chart.tsx +1 -1
  157. package/src/components/Globe/Globe.stories.tsx +23 -25
  158. package/src/components/Globe/Globe.tsx +1 -1
  159. package/src/components/Graph/CanvasForceGraph.stories.tsx +97 -0
  160. package/src/components/Graph/CanvasForceGraph.tsx +124 -0
  161. package/src/components/Graph/ForceGraph.stories.tsx +109 -41
  162. package/src/components/Graph/ForceGraph.tsx +105 -85
  163. package/src/components/Graph/{adapter.ts → graph-adapter.ts} +14 -8
  164. package/src/components/Graph/index.ts +1 -1
  165. package/src/components/Lattice/Lattice.stories.tsx +104 -0
  166. package/src/components/Lattice/Lattice.tsx +182 -0
  167. package/src/components/Lattice/index.ts +5 -0
  168. package/src/components/Tree/EdgeBundling.stories.tsx +144 -0
  169. package/src/components/Tree/Tree.stories.tsx +30 -42
  170. package/src/components/Tree/Tree.tsx +69 -95
  171. package/src/components/Tree/index.ts +2 -0
  172. package/src/components/Tree/layout/HierarchicalEdgeBundling.tsx +335 -0
  173. package/src/components/Tree/layout/RadialTree.tsx +242 -0
  174. package/src/components/Tree/layout/TidyTree.tsx +246 -0
  175. package/src/components/Tree/layout/hierarchy.ts +32 -0
  176. package/src/components/Tree/layout/index.ts +5 -5
  177. package/src/components/Tree/layout/slots.ts +19 -0
  178. package/src/components/Tree/layout/useContainerSize.ts +43 -0
  179. package/src/components/Tree/types/tree.test.ts +8 -7
  180. package/src/components/Tree/types/tree.ts +52 -36
  181. package/src/components/Tree/types/types.ts +38 -29
  182. package/src/components/index.ts +1 -4
  183. package/src/containers/ExplorerArticle/ExplorerArticle.stories.tsx +152 -0
  184. package/src/containers/ExplorerArticle/ExplorerArticle.tsx +120 -0
  185. package/src/containers/ExplorerArticle/Visualization.tsx +523 -0
  186. package/src/containers/ExplorerArticle/index.ts +5 -0
  187. package/src/containers/ExplorerArticle/variants.ts +47 -0
  188. package/src/containers/index.ts +7 -0
  189. package/src/hooks/useGraphModel.ts +25 -14
  190. package/src/index.ts +1 -4
  191. package/src/meta.ts +30 -8
  192. package/src/plugin.ts +9 -0
  193. package/src/{components/Tree/testing → testing}/generator.ts +6 -4
  194. package/src/testing/index.ts +9 -0
  195. package/src/testing/relations.ts +117 -0
  196. package/src/translations.ts +17 -12
  197. package/src/types/ExplorerAction.ts +20 -0
  198. package/src/types/Graph.ts +41 -0
  199. package/src/types/index.ts +2 -2
  200. package/src/typings.d.ts +8 -0
  201. package/src/util/index.ts +6 -0
  202. package/src/util/node-color.ts +23 -0
  203. package/src/{components → util}/plot.ts +16 -4
  204. package/src/vite-env.d.ts +10 -0
  205. package/dist/lib/browser/ExplorerContainer-5QHLD2B2.mjs +0 -37
  206. package/dist/lib/browser/ExplorerContainer-5QHLD2B2.mjs.map +0 -7
  207. package/dist/lib/browser/chunk-2MKBRIUT.mjs +0 -31
  208. package/dist/lib/browser/chunk-2MKBRIUT.mjs.map +0 -7
  209. package/dist/lib/browser/chunk-CZZ3DDR7.mjs +0 -38
  210. package/dist/lib/browser/chunk-CZZ3DDR7.mjs.map +0 -7
  211. package/dist/lib/browser/chunk-L4U4MPSZ.mjs +0 -190
  212. package/dist/lib/browser/chunk-L4U4MPSZ.mjs.map +0 -7
  213. package/dist/lib/browser/chunk-LGK64HLU.mjs +0 -11089
  214. package/dist/lib/browser/chunk-LGK64HLU.mjs.map +0 -7
  215. package/dist/lib/browser/chunk-UL5EDJPE.mjs +0 -21
  216. package/dist/lib/browser/chunk-UL5EDJPE.mjs.map +0 -7
  217. package/dist/lib/browser/index.mjs +0 -112
  218. package/dist/lib/browser/index.mjs.map +0 -7
  219. package/dist/lib/browser/intent-resolver-7MVEYNX7.mjs +0 -24
  220. package/dist/lib/browser/intent-resolver-7MVEYNX7.mjs.map +0 -7
  221. package/dist/lib/browser/meta.json +0 -1
  222. package/dist/lib/browser/react-surface-FABRDFTF.mjs +0 -31
  223. package/dist/lib/browser/react-surface-FABRDFTF.mjs.map +0 -7
  224. package/dist/lib/browser/types/index.mjs +0 -10
  225. package/dist/lib/node-esm/ExplorerContainer-AMYAVLO4.mjs +0 -38
  226. package/dist/lib/node-esm/ExplorerContainer-AMYAVLO4.mjs.map +0 -7
  227. package/dist/lib/node-esm/chunk-3ODK27PU.mjs +0 -33
  228. package/dist/lib/node-esm/chunk-3ODK27PU.mjs.map +0 -7
  229. package/dist/lib/node-esm/chunk-4GWDNZ4Z.mjs +0 -39
  230. package/dist/lib/node-esm/chunk-4GWDNZ4Z.mjs.map +0 -7
  231. package/dist/lib/node-esm/chunk-MCOXQ3ML.mjs +0 -192
  232. package/dist/lib/node-esm/chunk-MCOXQ3ML.mjs.map +0 -7
  233. package/dist/lib/node-esm/chunk-PIAXA43R.mjs +0 -23
  234. package/dist/lib/node-esm/chunk-PIAXA43R.mjs.map +0 -7
  235. package/dist/lib/node-esm/chunk-W4ZNCGOD.mjs.map +0 -7
  236. package/dist/lib/node-esm/index.mjs +0 -113
  237. package/dist/lib/node-esm/index.mjs.map +0 -7
  238. package/dist/lib/node-esm/intent-resolver-NL3SR2XF.mjs +0 -25
  239. package/dist/lib/node-esm/intent-resolver-NL3SR2XF.mjs.map +0 -7
  240. package/dist/lib/node-esm/meta.json +0 -1
  241. package/dist/lib/node-esm/meta.mjs +0 -10
  242. package/dist/lib/node-esm/react-surface-EYCZUAAI.mjs +0 -32
  243. package/dist/lib/node-esm/react-surface-EYCZUAAI.mjs.map +0 -7
  244. package/dist/lib/node-esm/types/index.mjs +0 -11
  245. package/dist/types/src/capabilities/intent-resolver.d.ts +0 -4
  246. package/dist/types/src/capabilities/intent-resolver.d.ts.map +0 -1
  247. package/dist/types/src/components/ExplorerContainer.d.ts +0 -9
  248. package/dist/types/src/components/ExplorerContainer.d.ts.map +0 -1
  249. package/dist/types/src/components/Graph/D3ForceGraph.d.ts +0 -14
  250. package/dist/types/src/components/Graph/D3ForceGraph.d.ts.map +0 -1
  251. package/dist/types/src/components/Graph/D3ForceGraph.stories.d.ts +0 -6
  252. package/dist/types/src/components/Graph/D3ForceGraph.stories.d.ts.map +0 -1
  253. package/dist/types/src/components/Graph/adapter.d.ts.map +0 -1
  254. package/dist/types/src/components/Graph/testing.d.ts +0 -14
  255. package/dist/types/src/components/Graph/testing.d.ts.map +0 -1
  256. package/dist/types/src/components/Tree/testing/generator.d.ts.map +0 -1
  257. package/dist/types/src/components/Tree/testing/index.d.ts +0 -2
  258. package/dist/types/src/components/Tree/testing/index.d.ts.map +0 -1
  259. package/dist/types/src/components/plot.d.ts.map +0 -1
  260. package/dist/types/src/types/schema.d.ts +0 -12
  261. package/dist/types/src/types/schema.d.ts.map +0 -1
  262. package/dist/types/src/types/types.d.ts +0 -18
  263. package/dist/types/src/types/types.d.ts.map +0 -1
  264. package/src/capabilities/intent-resolver.ts +0 -19
  265. package/src/components/ExplorerContainer.tsx +0 -37
  266. package/src/components/Graph/D3ForceGraph.stories.tsx +0 -65
  267. package/src/components/Graph/D3ForceGraph.tsx +0 -101
  268. package/src/components/Graph/testing.ts +0 -55
  269. package/src/components/Tree/layout/HierarchicalEdgeBundling.ts +0 -162
  270. package/src/components/Tree/layout/RadialTree.ts +0 -94
  271. package/src/components/Tree/layout/TidyTree.ts +0 -101
  272. package/src/components/Tree/testing/index.ts +0 -5
  273. package/src/types/schema.ts +0 -16
  274. package/src/types/types.ts +0 -22
  275. /package/dist/lib/{browser/meta.mjs.map → neutral/ExplorerPlugin.mjs.map} +0 -0
  276. /package/dist/lib/{browser/types/index.mjs.map → neutral/chunk-J5LGTIGS.mjs.map} +0 -0
  277. /package/dist/lib/{node-esm/types → neutral}/index.mjs.map +0 -0
  278. /package/dist/lib/{node-esm → neutral}/meta.mjs.map +0 -0
@@ -1,17 +1,16 @@
1
- import { createRequire } from 'node:module';const require = createRequire(import.meta.url);
1
+ import {
2
+ treeTypeToTreeNode
3
+ } from "../chunk-7XUDLV6E.mjs";
4
+ import {
5
+ createAdapter,
6
+ getNodeFillForObject
7
+ } from "../chunk-IKHJV3Q4.mjs";
8
+ import "../chunk-J5LGTIGS.mjs";
2
9
 
3
10
  // src/components/Chart/Chart.tsx
4
- import { useSignals as _useSignals } from "@preact-signals/safe-react/tracking";
5
11
  import * as Plot from "@observablehq/plot";
6
12
  import React, { useEffect } from "react";
7
13
  import { useResizeDetector } from "react-resize-detector";
8
-
9
- // src/components/plot.ts
10
- var createAdapter = (prop, accessor) => accessor ? {
11
- transform: (values) => values.map((value) => accessor(value)[prop])
12
- } : prop;
13
-
14
- // src/components/Chart/Chart.tsx
15
14
  var defaultOptions = {
16
15
  r: 4,
17
16
  stroke: "gray",
@@ -19,49 +18,43 @@ var defaultOptions = {
19
18
  fillOpacity: 0.2
20
19
  };
21
20
  var Chart = ({ items = [], accessor, options = defaultOptions }) => {
22
- var _effect = _useSignals();
23
- try {
24
- const { ref: containerRef, width = 0, height = 0 } = useResizeDetector({
25
- refreshRate: 200
26
- });
27
- useEffect(() => {
28
- if (!width || !height) {
29
- return;
30
- }
31
- const plot3 = Plot.plot({
32
- grid: true,
33
- width,
34
- height,
35
- style: {
36
- background: "transparent"
37
- },
38
- marks: [
39
- Plot.frame(),
40
- Plot.dot(items, {
41
- x: createAdapter("x", accessor),
42
- y: createAdapter("y", accessor),
43
- ...options
44
- })
45
- ]
46
- });
47
- containerRef.current.append(plot3);
48
- return () => plot3?.remove();
49
- }, [
50
- items,
21
+ const { ref: containerRef, width = 0, height = 0 } = useResizeDetector({
22
+ refreshRate: 200
23
+ });
24
+ useEffect(() => {
25
+ if (!width || !height) {
26
+ return;
27
+ }
28
+ const plot3 = Plot.plot({
29
+ grid: true,
51
30
  width,
52
- height
53
- ]);
54
- return /* @__PURE__ */ React.createElement("div", {
55
- ref: containerRef,
56
- className: "grow"
31
+ height,
32
+ style: {
33
+ background: "transparent"
34
+ },
35
+ marks: [
36
+ Plot.frame(),
37
+ Plot.dot(items, {
38
+ x: createAdapter("x", accessor),
39
+ y: createAdapter("y", accessor),
40
+ ...options
41
+ })
42
+ ]
57
43
  });
58
- } finally {
59
- _effect.f();
60
- }
44
+ containerRef.current.append(plot3);
45
+ return () => plot3?.remove();
46
+ }, [
47
+ items,
48
+ width,
49
+ height
50
+ ]);
51
+ return /* @__PURE__ */ React.createElement("div", {
52
+ ref: containerRef,
53
+ className: "grow"
54
+ });
61
55
  };
62
56
 
63
57
  // src/components/Globe/Globe.tsx
64
- import { useSignals as _useSignals2 } from "@preact-signals/safe-react/tracking";
65
58
  import * as Plot2 from "@observablehq/plot";
66
59
  import React2, { useEffect as useEffect2 } from "react";
67
60
  import { useResizeDetector as useResizeDetector2 } from "react-resize-detector";
@@ -10775,180 +10768,797 @@ var defaultOptions2 = {
10775
10768
  fill: "#003300"
10776
10769
  };
10777
10770
  var Globe = ({ items = [], accessor, projection = "orthographic", options = defaultOptions2 }) => {
10778
- var _effect = _useSignals2();
10779
- try {
10780
- const { ref: containerRef, width = 0, height = 0 } = useResizeDetector2({
10781
- refreshRate: 200
10771
+ const { ref: containerRef, width = 0, height = 0 } = useResizeDetector2({
10772
+ refreshRate: 200
10773
+ });
10774
+ const land = topojson.feature(countries_110m_default, countries_110m_default.objects.land);
10775
+ useEffect2(() => {
10776
+ if (!width || !height) {
10777
+ return;
10778
+ }
10779
+ const plot3 = Plot2.plot({
10780
+ // https://observablehq.com/plot/features/projections
10781
+ projection: {
10782
+ type: projection,
10783
+ rotate: [
10784
+ -100,
10785
+ -20
10786
+ ]
10787
+ },
10788
+ // projection: { type: 'equirectangular', rotate: [-140, -30] },
10789
+ width,
10790
+ height,
10791
+ style: {
10792
+ background: "transparent"
10793
+ },
10794
+ // TODO(burdon): Create simple wrapper for Plot with good defaults.
10795
+ marks: [
10796
+ Plot2.sphere({
10797
+ fill: "lightblue",
10798
+ fillOpacity: 0.5
10799
+ }),
10800
+ Plot2.geo(land, {
10801
+ fill: "darkgreen",
10802
+ fillOpacity: 0.5
10803
+ }),
10804
+ Plot2.graticule(),
10805
+ Plot2.dot(items, {
10806
+ x: createAdapter("lat", accessor),
10807
+ y: createAdapter("lng", accessor),
10808
+ ...options
10809
+ })
10810
+ ]
10782
10811
  });
10783
- const land = topojson.feature(countries_110m_default, countries_110m_default.objects.land);
10784
- useEffect2(() => {
10785
- if (!width || !height) {
10812
+ containerRef.current.append(plot3);
10813
+ return () => plot3?.remove();
10814
+ }, [
10815
+ items,
10816
+ width,
10817
+ height
10818
+ ]);
10819
+ return /* @__PURE__ */ React2.createElement("div", {
10820
+ ref: containerRef,
10821
+ className: "grow p-4"
10822
+ });
10823
+ };
10824
+
10825
+ // src/components/Graph/CanvasForceGraph.tsx
10826
+ import { forceLink, forceManyBody } from "d3";
10827
+ import NativeForceGraph from "force-graph";
10828
+ import React3, { useCallback, useEffect as useEffect3, useRef, useState } from "react";
10829
+ import { useResizeDetector as useResizeDetector3 } from "react-resize-detector";
10830
+ import { composable, composableProps } from "@dxos/react-ui";
10831
+
10832
+ // src/components/Graph/graph-adapter.ts
10833
+ var GraphAdapter = class {
10834
+ graph;
10835
+ _nodes = [];
10836
+ _links = [];
10837
+ constructor(graph) {
10838
+ this.graph = graph;
10839
+ this._nodes = graph.nodes.map((node) => ({
10840
+ id: node.id,
10841
+ type: node.type,
10842
+ data: node.data
10843
+ }));
10844
+ const nodeIds = new Set(this._nodes.map((node) => node.id));
10845
+ this._links = graph.edges.filter((edge) => nodeIds.has(edge.source) && nodeIds.has(edge.target)).map((edge) => ({
10846
+ type: edge.type,
10847
+ source: edge.source,
10848
+ target: edge.target,
10849
+ data: edge.data
10850
+ }));
10851
+ }
10852
+ get nodes() {
10853
+ return this._nodes;
10854
+ }
10855
+ get links() {
10856
+ return this._links;
10857
+ }
10858
+ };
10859
+
10860
+ // src/components/Graph/CanvasForceGraph.tsx
10861
+ var CanvasForceGraph = composable(({ model, match, onClick, ...props }, forwardedRef) => {
10862
+ const { ref: resizeRef, width, height } = useResizeDetector3({
10863
+ refreshRate: 200
10864
+ });
10865
+ const setRef = useCallback((node) => {
10866
+ resizeRef(node);
10867
+ assignRef(forwardedRef, node);
10868
+ }, [
10869
+ resizeRef,
10870
+ forwardedRef
10871
+ ]);
10872
+ const rootRef = useRef(null);
10873
+ const forceGraph = useRef(null);
10874
+ const [data, setData] = useState();
10875
+ useEffect3(() => {
10876
+ return model?.subscribe((model2) => setData(new GraphAdapter(model2.graph)));
10877
+ }, [
10878
+ model
10879
+ ]);
10880
+ useEffect3(() => {
10881
+ if (rootRef.current) {
10882
+ forceGraph.current = new NativeForceGraph(rootRef.current).nodeRelSize(6).nodeLabel((node) => node.type === "schema" ? node.data.typename : node.data.label ?? node.id).nodeAutoColorBy((node) => node.type === "schema" ? "schema" : node.data.typename).linkAutoColorBy((link) => link.type);
10883
+ }
10884
+ return () => {
10885
+ forceGraph.current?.pauseAnimation().graphData({
10886
+ nodes: [],
10887
+ links: []
10888
+ });
10889
+ forceGraph.current = null;
10890
+ };
10891
+ }, []);
10892
+ useEffect3(() => {
10893
+ if (!data || !width || !height || !forceGraph.current) {
10894
+ return;
10895
+ }
10896
+ forceGraph.current.pauseAnimation().width(width).height(height).onEngineStop(() => handleZoomToFit()).onNodeClick((node) => {
10897
+ forceGraph.current?.emitParticle(node);
10898
+ }).d3Force("link", forceLink().distance(160).strength(0.5)).d3Force("charge", forceManyBody().strength(-30)).graphData(data).warmupTicks(100).cooldownTime(1e3).resumeAnimation();
10899
+ }, [
10900
+ data,
10901
+ width,
10902
+ height
10903
+ ]);
10904
+ const handleZoomToFit = () => {
10905
+ forceGraph.current?.zoomToFit(400, 40);
10906
+ };
10907
+ const handleClick = useCallback((event) => {
10908
+ onClick?.(event);
10909
+ if (!event.defaultPrevented) {
10910
+ handleZoomToFit();
10911
+ }
10912
+ }, [
10913
+ onClick
10914
+ ]);
10915
+ return /* @__PURE__ */ React3.createElement("div", {
10916
+ ...composableProps(props, {
10917
+ classNames: "relative grow"
10918
+ }),
10919
+ onClick: handleClick,
10920
+ ref: setRef
10921
+ }, /* @__PURE__ */ React3.createElement("div", {
10922
+ ref: rootRef,
10923
+ className: "absolute inset-0"
10924
+ }));
10925
+ });
10926
+ var assignRef = (ref, value) => {
10927
+ if (typeof ref === "function") {
10928
+ ref(value);
10929
+ } else if (ref) {
10930
+ ref.current = value;
10931
+ }
10932
+ };
10933
+
10934
+ // src/components/Graph/ForceGraph.tsx
10935
+ import { Atom, useAtomValue } from "@effect-atom/atom-react";
10936
+ import React4, { useCallback as useCallback2, useEffect as useEffect4, useMemo, useRef as useRef2, useState as useState2 } from "react";
10937
+ import { Obj } from "@dxos/echo";
10938
+ import { SelectionModel } from "@dxos/graph";
10939
+ import { composable as composable2, composableProps as composableProps2 } from "@dxos/react-ui";
10940
+ import { GraphForceProjector, SVG } from "@dxos/react-ui-graph";
10941
+ import { getHashStyles } from "@dxos/ui-theme";
10942
+ import "@dxos/react-ui-graph/styles/graph.css";
10943
+ var EMPTY_ATOM = Atom.make({
10944
+ nodes: [],
10945
+ edges: []
10946
+ });
10947
+ var ForceGraph = composable2(({ model, selection: selectionProp, grid, drag, onInspect, ...props }, forwardedRef) => {
10948
+ useAtomValue(model?.graphAtom ?? EMPTY_ATOM);
10949
+ const graph = useRef2(null);
10950
+ const selection = useMemo(() => selectionProp ?? new SelectionModel(), [
10951
+ selectionProp
10952
+ ]);
10953
+ useEffect4(() => {
10954
+ const unsubscribe = selection.subscribe(() => graph.current?.repaint());
10955
+ return unsubscribe;
10956
+ }, [
10957
+ selection
10958
+ ]);
10959
+ const svgRef = useRef2(null);
10960
+ const [projector, setProjector] = useState2();
10961
+ useEffect4(() => {
10962
+ if (svgRef.current) {
10963
+ setProjector(new GraphForceProjector(svgRef.current, {
10964
+ attributes: {
10965
+ // TODO(burdon): Check type (currently assumes Employee property).
10966
+ // Edge shouldn't contribute to force if it's not active.
10967
+ linkForce: (edge) => edge.data?.object?.active !== false
10968
+ },
10969
+ forces: {
10970
+ point: {
10971
+ strength: 0.01
10972
+ }
10973
+ }
10974
+ }));
10975
+ }
10976
+ }, []);
10977
+ const handleSelect = useCallback2((node) => {
10978
+ if (selection.contains(node.id)) {
10979
+ selection.remove(node.id);
10980
+ } else {
10981
+ selection.add(node.id);
10982
+ }
10983
+ }, [
10984
+ selection
10985
+ ]);
10986
+ return /* @__PURE__ */ React4.createElement("div", {
10987
+ ...composableProps2(props, {
10988
+ classNames: "dx-container"
10989
+ }),
10990
+ ref: forwardedRef
10991
+ }, /* @__PURE__ */ React4.createElement(SVG.Root, {
10992
+ ref: svgRef
10993
+ }, /* @__PURE__ */ React4.createElement(SVG.Markers, null), grid && /* @__PURE__ */ React4.createElement(SVG.Grid, {
10994
+ axis: true
10995
+ }), /* @__PURE__ */ React4.createElement(SVG.Zoom, {
10996
+ extent: [
10997
+ 1 / 2,
10998
+ 2
10999
+ ]
11000
+ }, /* @__PURE__ */ React4.createElement(SVG.Graph, {
11001
+ ref: graph,
11002
+ drag,
11003
+ model,
11004
+ projector,
11005
+ labels: {
11006
+ text: (node) => node.data?.data.label ?? node.id
11007
+ },
11008
+ attributes: {
11009
+ node: (node) => {
11010
+ const obj = node.data?.data.object;
11011
+ return {
11012
+ data: {
11013
+ color: getHashStyles(obj && Obj.getTypename(obj))?.hue
11014
+ },
11015
+ classes: {
11016
+ "dx-selected": selection.contains(node.id)
11017
+ }
11018
+ };
11019
+ }
11020
+ },
11021
+ onSelect: handleSelect,
11022
+ onInspect
11023
+ }))));
11024
+ });
11025
+
11026
+ // src/components/Lattice/Lattice.tsx
11027
+ import { select } from "d3";
11028
+ import React5, { useEffect as useEffect6, useMemo as useMemo2, useRef as useRef3 } from "react";
11029
+ import { Obj as Obj2 } from "@dxos/echo";
11030
+
11031
+ // src/components/Tree/layout/useContainerSize.ts
11032
+ import { useEffect as useEffect5, useState as useState3 } from "react";
11033
+ var useContainerSize = () => {
11034
+ const [el, setEl] = useState3(null);
11035
+ const [size, setSize] = useState3({
11036
+ width: 0,
11037
+ height: 0
11038
+ });
11039
+ useEffect5(() => {
11040
+ if (!el) {
11041
+ return;
11042
+ }
11043
+ const rect = el.getBoundingClientRect();
11044
+ setSize({
11045
+ width: rect.width,
11046
+ height: rect.height
11047
+ });
11048
+ const observer = new ResizeObserver((entries) => {
11049
+ const entry = entries[0];
11050
+ if (!entry) {
10786
11051
  return;
10787
11052
  }
10788
- const plot3 = Plot2.plot({
10789
- // https://observablehq.com/plot/features/projections
10790
- projection: {
10791
- type: projection,
10792
- rotate: [
10793
- -100,
10794
- -20
10795
- ]
10796
- },
10797
- // projection: { type: 'equirectangular', rotate: [-140, -30] },
11053
+ const { width, height } = entry.contentRect;
11054
+ setSize((prev) => prev.width === width && prev.height === height ? prev : {
10798
11055
  width,
10799
- height,
10800
- style: {
10801
- background: "transparent"
10802
- },
10803
- // TODO(burdon): Create simple wrapper for Plot with good defaults.
10804
- marks: [
10805
- Plot2.sphere({
10806
- fill: "lightblue",
10807
- fillOpacity: 0.5
10808
- }),
10809
- Plot2.geo(land, {
10810
- fill: "darkgreen",
10811
- fillOpacity: 0.5
10812
- }),
10813
- Plot2.graticule(),
10814
- Plot2.dot(items, {
10815
- x: createAdapter("lat", accessor),
10816
- y: createAdapter("lng", accessor),
10817
- ...options
10818
- })
10819
- ]
11056
+ height
10820
11057
  });
10821
- containerRef.current.append(plot3);
10822
- return () => plot3?.remove();
10823
- }, [
10824
- items,
11058
+ });
11059
+ observer.observe(el);
11060
+ return () => observer.disconnect();
11061
+ }, [
11062
+ el
11063
+ ]);
11064
+ return {
11065
+ setRef: setEl,
11066
+ width: size.width,
11067
+ height: size.height
11068
+ };
11069
+ };
11070
+
11071
+ // src/components/Lattice/Lattice.tsx
11072
+ var TRANSITION_MS = 350;
11073
+ var Lattice = ({ nodes, padding = 16, onNodeHover }) => {
11074
+ const svgRef = useRef3(null);
11075
+ const { setRef, width, height } = useContainerSize();
11076
+ const cells = useMemo2(() => {
11077
+ return nodes.map((node) => {
11078
+ const object = node.data?.object;
11079
+ if (!object) {
11080
+ return void 0;
11081
+ }
11082
+ const label = node.data?.label ?? Obj2.getLabel(object) ?? node.id;
11083
+ const typename = Obj2.getTypename(object) ?? "(untyped)";
11084
+ return {
11085
+ id: node.id,
11086
+ label,
11087
+ typename,
11088
+ object
11089
+ };
11090
+ }).filter((cell) => cell !== void 0).sort((a, b) => a.typename.localeCompare(b.typename) || a.label.localeCompare(b.label));
11091
+ }, [
11092
+ nodes
11093
+ ]);
11094
+ const handleHoverRef = useRef3(void 0);
11095
+ handleHoverRef.current = onNodeHover;
11096
+ useEffect6(() => {
11097
+ if (!svgRef.current || !width || !height) {
11098
+ return;
11099
+ }
11100
+ renderLattice(svgRef.current, cells, {
10825
11101
  width,
10826
- height
10827
- ]);
10828
- return /* @__PURE__ */ React2.createElement("div", {
10829
- ref: containerRef,
10830
- className: "grow p-4"
11102
+ height,
11103
+ padding,
11104
+ onNodeHover: (n, e) => handleHoverRef.current?.(n, e)
10831
11105
  });
10832
- } finally {
10833
- _effect.f();
11106
+ return () => {
11107
+ handleHoverRef.current?.(null);
11108
+ };
11109
+ }, [
11110
+ cells,
11111
+ width,
11112
+ height,
11113
+ padding
11114
+ ]);
11115
+ return /* @__PURE__ */ React5.createElement("div", {
11116
+ ref: setRef,
11117
+ className: "dx-expander relative"
11118
+ }, width > 0 && height > 0 && /* @__PURE__ */ React5.createElement("svg", {
11119
+ ref: svgRef,
11120
+ xmlns: "http://www.w3.org/2000/svg",
11121
+ width,
11122
+ height,
11123
+ viewBox: `0 0 ${width} ${height}`
11124
+ }));
11125
+ };
11126
+ var renderLattice = (svgElement, cells, options) => {
11127
+ const { width, height, padding, onNodeHover } = options;
11128
+ const svg = select(svgElement);
11129
+ if (!cells.length) {
11130
+ onNodeHover(null);
11131
+ svg.selectAll("g.dx-lattice-root").remove();
11132
+ return;
10834
11133
  }
11134
+ const count = cells.length;
11135
+ const columns = Math.max(1, Math.ceil(Math.sqrt(count)));
11136
+ const rows = Math.ceil(count / columns);
11137
+ const innerW = Math.max(0, width - 2 * padding);
11138
+ const innerH = Math.max(0, height - 2 * padding);
11139
+ const cellSize = Math.max(0, Math.min(innerW / columns, innerH / rows));
11140
+ const gutter = Math.max(2, cellSize * 0.12);
11141
+ const rectSize = Math.max(0, cellSize - gutter);
11142
+ const radius = Math.max(2, rectSize * 0.18);
11143
+ const gridW = cellSize * columns;
11144
+ const gridH = cellSize * rows;
11145
+ const offsetX = (width - gridW) / 2;
11146
+ const offsetY = (height - gridH) / 2;
11147
+ const g = svg.selectAll("g.dx-lattice-root").data([
11148
+ null
11149
+ ]).join("g").classed("dx-lattice-root", true);
11150
+ const positioned = cells.map((cell, i) => ({
11151
+ ...cell,
11152
+ x: offsetX + i % columns * cellSize + gutter / 2,
11153
+ y: offsetY + Math.floor(i / columns) * cellSize + gutter / 2
11154
+ }));
11155
+ const node = g.selectAll("g.dx-lattice-cell").data(positioned, (d) => d.id).join((enter) => {
11156
+ const ge = enter.append("g").classed("dx-lattice-cell", true).attr("opacity", 0);
11157
+ ge.append("rect").style("cursor", "pointer");
11158
+ return ge;
11159
+ }, (update) => update, (exit) => exit.each(function() {
11160
+ select(this).interrupt();
11161
+ }).transition().duration(TRANSITION_MS).attr("opacity", 0).remove());
11162
+ node.transition().duration(TRANSITION_MS).attr("opacity", 1).attr("transform", (d) => `translate(${d.x},${d.y})`);
11163
+ node.select("rect").attr("width", rectSize).attr("height", rectSize).attr("rx", radius).attr("ry", radius).style("fill", (d) => getNodeFillForObject(d.object)).on("pointerenter", (event, d) => onNodeHover({
11164
+ id: d.id,
11165
+ label: d.label,
11166
+ data: d.object
11167
+ }, event)).on("pointerleave", () => onNodeHover(null));
10835
11168
  };
10836
11169
 
10837
11170
  // src/components/Tree/Tree.tsx
10838
- import { useSignals as _useSignals3 } from "@preact-signals/safe-react/tracking";
10839
- import React3, { useEffect as useEffect3, useRef, useState } from "react";
10840
- import { useAsyncState } from "@dxos/react-ui";
10841
- import { SVG } from "@dxos/react-ui-graph";
10842
- import { SpaceGraphModel } from "@dxos/schema";
11171
+ import React9, { useMemo as useMemo6 } from "react";
10843
11172
 
10844
- // src/components/Tree/layout/HierarchicalEdgeBundling.ts
10845
- import { cluster, curveBundle, hierarchy, lineRadial, select } from "d3";
10846
- var HierarchicalEdgeBundling = (s, data, options) => {
10847
- const svg = select(s);
10848
- svg.selectAll("*").remove();
10849
- const { radius = 600, padding = 100, slots } = options;
10850
- const root = hierarchy(flatten(data));
10851
- const tree3 = cluster().size([
10852
- 2 * Math.PI,
10853
- radius - padding
11173
+ // src/components/Tree/layout/HierarchicalEdgeBundling.tsx
11174
+ import { cluster, curveBundle, hierarchy, lineRadial, select as select2 } from "d3";
11175
+ import React6, { useEffect as useEffect7, useMemo as useMemo3, useRef as useRef4 } from "react";
11176
+ import { mx } from "@dxos/ui-theme";
11177
+
11178
+ // src/components/Tree/layout/slots.ts
11179
+ var defaultTreeLayoutSlots = {
11180
+ // Cursor + transition so the hover swap reads clearly; SVG circles support the `:hover` pseudo-class
11181
+ // via Tailwind variants exactly like HTML elements.
11182
+ node: "fill-blue-600 hover:fill-orange-500 cursor-pointer transition-colors",
11183
+ // 0.5px is fine on a white background, but on a dark Storybook background the lines disappear.
11184
+ // Use stroke-1 with opacity 50% so they read in both themes; dx-bundle-dim/out/in further tune on hover.
11185
+ path: "fill-none stroke-blue-500/50 stroke-[1px] dark:stroke-blue-400/60",
11186
+ text: "fill-neutral-700 dark:fill-neutral-300 text-xs hover:fill-orange-500 cursor-pointer transition-colors"
11187
+ };
11188
+
11189
+ // src/components/Tree/layout/HierarchicalEdgeBundling.tsx
11190
+ var TRANSITION_MS2 = 350;
11191
+ var HierarchicalEdgeBundling = ({ classNames, data, edges = [], label = (d) => d.label ?? d.id, padding = 120, tension = 0.85, r = 4, slots = defaultTreeLayoutSlots, onNodeHover }) => {
11192
+ const svgRef = useRef4(null);
11193
+ const { setRef, width, height } = useContainerSize();
11194
+ const root = useMemo3(() => buildBundleHierarchy(data, edges), [
11195
+ data,
11196
+ edges
10854
11197
  ]);
10855
- const layout = tree3(addLinks(root));
10856
- const node = svg.append("g").selectAll().data(layout.leaves()).join("g").attr("transform", (d) => `rotate(${d.x * (180 / Math.PI) - 90}) translate(${d.y},0)`).append("text").attr("class", slots?.text ?? "").attr("dy", "0.31em").attr("x", (d) => d.x < Math.PI ? 6 : -6).attr("text-anchor", (d) => d.x < Math.PI ? "start" : "end").attr("transform", (d) => d.x >= Math.PI ? "rotate(180)" : null).call((text) => text.text((d) => d.data.id.slice(0, 8)));
10857
- const line = lineRadial().curve(curveBundle.beta(0.85)).radius((d) => d.y).angle((d) => d.x);
10858
- const links = svg.append("g").selectAll().data(layout.leaves().flatMap((leaf) => leaf.outgoing)).join("path").style("mix-blend-mode", "multiply").attr("class", slots?.path ?? "").attr("d", ([i, o]) => {
10859
- return line(i.path(o));
10860
- }).each(function(d) {
10861
- d.path = this;
11198
+ const handleHoverRef = useRef4(() => {
10862
11199
  });
10863
- };
10864
- var addLinks = (root) => {
10865
- const nodes = new Map(root.descendants().map((d) => [
10866
- d.data.id,
10867
- d
10868
- ]));
10869
- const parents = root.descendants().reduce((map, d) => {
10870
- if (d.children?.length) {
10871
- map.set(d.data.id, d);
11200
+ handleHoverRef.current = (node, event) => onNodeHover?.(node, event);
11201
+ useEffect7(() => {
11202
+ if (!svgRef.current || !width || !height) {
11203
+ return;
10872
11204
  }
10873
- return map;
10874
- }, /* @__PURE__ */ new Map());
10875
- for (const d of root.leaves()) {
10876
- const parent = parents.get(d.data.id);
10877
- if (parent) {
10878
- d.outgoing = parent.data.children?.slice(1).map((child) => {
10879
- return [
10880
- d,
10881
- nodes.get(child.id)
10882
- ];
10883
- }) ?? [];
10884
- } else {
10885
- d.outgoing = [];
11205
+ const radius = Math.max(0, Math.min(width, height) / 2 - padding);
11206
+ renderBundling(svgRef.current, root, {
11207
+ radius,
11208
+ r,
11209
+ label,
11210
+ slots,
11211
+ tension,
11212
+ onNodeHover: (n, e) => handleHoverRef.current(n, e)
11213
+ });
11214
+ }, [
11215
+ root,
11216
+ width,
11217
+ height,
11218
+ padding,
11219
+ tension,
11220
+ r,
11221
+ label,
11222
+ slots
11223
+ ]);
11224
+ return /* @__PURE__ */ React6.createElement("div", {
11225
+ ref: setRef,
11226
+ className: mx("dx-expander relative", classNames)
11227
+ }, width > 0 && height > 0 && /* @__PURE__ */ React6.createElement("svg", {
11228
+ ref: svgRef,
11229
+ xmlns: "http://www.w3.org/2000/svg",
11230
+ width,
11231
+ height,
11232
+ viewBox: `${-width / 2} ${-height / 2} ${width} ${height}`
11233
+ }));
11234
+ };
11235
+ var buildBundleHierarchy = (data, edges) => {
11236
+ const root = hierarchy(data);
11237
+ const byId = /* @__PURE__ */ new Map();
11238
+ for (const node of root.descendants()) {
11239
+ byId.set(node.data.id, node);
11240
+ node.outgoing = [];
11241
+ node.incoming = [];
11242
+ }
11243
+ for (const edge of edges) {
11244
+ const source = byId.get(edge.source);
11245
+ const target = byId.get(edge.target);
11246
+ if (!source || !target || source === target) {
11247
+ continue;
10886
11248
  }
11249
+ source.outgoing.push([
11250
+ source,
11251
+ target,
11252
+ edge
11253
+ ]);
11254
+ target.incoming.push([
11255
+ source,
11256
+ target,
11257
+ edge
11258
+ ]);
10887
11259
  }
10888
11260
  return root;
10889
11261
  };
10890
- var flatten = (node) => {
10891
- const clone = {
10892
- id: node.id
11262
+ var renderBundling = (svgElement, root, options) => {
11263
+ const { radius, r, tension, label, slots, onNodeHover } = options;
11264
+ const svg = select2(svgElement);
11265
+ if (!root.children?.length) {
11266
+ svg.selectAll("g.dx-bundle-root").remove();
11267
+ return;
11268
+ }
11269
+ cluster().size([
11270
+ 2 * Math.PI,
11271
+ radius
11272
+ ])(root);
11273
+ const g = svg.selectAll("g.dx-bundle-root").data([
11274
+ null
11275
+ ]).join("g").classed("dx-bundle-root", true);
11276
+ const linksLayer = g.selectAll("g.dx-bundle-links").data([
11277
+ null
11278
+ ]).join("g").classed("dx-bundle-links", true);
11279
+ const nodesLayer = g.selectAll("g.dx-bundle-nodes").data([
11280
+ null
11281
+ ]).join("g").classed("dx-bundle-nodes", true);
11282
+ const line = lineRadial().curve(curveBundle.beta(tension)).radius((d) => d.y).angle((d) => d.x);
11283
+ const leaves = root.leaves();
11284
+ const flatEdges = leaves.flatMap((leaf) => leaf.outgoing ?? []);
11285
+ const paths = linksLayer.selectAll("path").data(flatEdges, (d) => `${d[0].data.id}->${d[1].data.id}`).join((enter) => enter.append("path").attr("class", slots.path ?? "").attr("fill", "none").attr("opacity", 0), (update) => update, (exit) => exit.each(function() {
11286
+ select2(this).interrupt();
11287
+ }).transition().duration(TRANSITION_MS2).attr("opacity", 0).remove());
11288
+ paths.each(function(d) {
11289
+ d[0].pathEl = this;
11290
+ }).transition().duration(TRANSITION_MS2).attr("opacity", 1).attr("d", ([s, t]) => line(s.path(t)));
11291
+ const labels = nodesLayer.selectAll("g.dx-bundle-leaf").data(leaves, (d) => d.data.id).join((enter) => {
11292
+ const ge = enter.append("g").classed("dx-bundle-leaf", true).attr("opacity", 0);
11293
+ ge.append("circle").style("cursor", "pointer");
11294
+ ge.append("text").attr("dy", "0.32em").attr("paint-order", "stroke").style("cursor", "pointer");
11295
+ return ge;
11296
+ }, (update) => update, (exit) => exit.each(function() {
11297
+ select2(this).interrupt();
11298
+ }).transition().duration(TRANSITION_MS2).attr("opacity", 0).remove());
11299
+ labels.transition().duration(TRANSITION_MS2).attr("opacity", 1).attr("transform", (d) => `rotate(${d.x * 180 / Math.PI - 90}) translate(${d.y},0)`);
11300
+ const onEnter = function(event, d) {
11301
+ onNodeHover(d.data, event);
11302
+ hover(linksLayer, leaves, d, true);
10893
11303
  };
10894
- if (node.children?.length) {
10895
- const children = node.children.map((child) => flatten(child));
10896
- clone.children = [
10897
- {
10898
- id: node.id
10899
- },
10900
- ...children
10901
- ];
11304
+ const onLeave = function(event, d) {
11305
+ onNodeHover(null);
11306
+ hover(linksLayer, leaves, d, false);
11307
+ };
11308
+ labels.select("circle").attr("class", [
11309
+ slots.node ?? "",
11310
+ "dx-leaf"
11311
+ ].filter(Boolean).join(" ")).attr("r", r).style("fill", (d) => getNodeFillForObject(d.data.data)).each(function(d) {
11312
+ d.circle = this;
11313
+ }).on("pointerenter", onEnter).on("pointerleave", onLeave);
11314
+ labels.select("text").attr("class", slots.text ?? "").attr("x", (d) => d.x < Math.PI ? r + 4 : -(r + 4)).attr("text-anchor", (d) => d.x < Math.PI ? "start" : "end").attr("transform", (d) => d.x >= Math.PI ? "rotate(180)" : null).each(function(d) {
11315
+ d.text = this;
11316
+ }).text((d) => label(d.data)).on("pointerenter", onEnter).on("pointerleave", onLeave);
11317
+ };
11318
+ var hover = (linksLayer, leaves, focused, on) => {
11319
+ const outgoing = new Set((focused.outgoing ?? []).map(([, t]) => t));
11320
+ const incoming = new Set((focused.incoming ?? []).map(([s]) => s));
11321
+ linksLayer.selectAll("path").style("stroke", (d) => {
11322
+ if (!on) {
11323
+ return null;
11324
+ }
11325
+ if (d[0] === focused) {
11326
+ return "var(--color-orange-500)";
11327
+ }
11328
+ if (d[1] === focused) {
11329
+ return "var(--color-sky-500)";
11330
+ }
11331
+ return null;
11332
+ }).style("stroke-width", (d) => on && (d[0] === focused || d[1] === focused) ? "1.5px" : null).style("opacity", (d) => on && d[0] !== focused && d[1] !== focused ? "0.08" : null);
11333
+ for (const leaf of leaves) {
11334
+ const isOut = outgoing.has(leaf);
11335
+ const isIn = incoming.has(leaf);
11336
+ const isConnected = isOut || isIn;
11337
+ if (leaf.text) {
11338
+ select2(leaf.text).style("fill", () => {
11339
+ if (!on) {
11340
+ return null;
11341
+ }
11342
+ if (leaf === focused) {
11343
+ return "var(--color-neutral-900)";
11344
+ }
11345
+ if (isOut) {
11346
+ return "var(--color-orange-500)";
11347
+ }
11348
+ if (isIn) {
11349
+ return "var(--color-sky-500)";
11350
+ }
11351
+ return null;
11352
+ }).style("font-weight", () => on && leaf === focused ? "600" : null);
11353
+ }
11354
+ if (leaf.circle) {
11355
+ select2(leaf.circle).style("stroke", () => on && isConnected ? "var(--color-orange-400)" : null).style("stroke-width", () => on && isConnected ? "2.5px" : null).style("opacity", () => on && !isConnected && leaf !== focused ? "0.15" : null);
11356
+ }
10902
11357
  }
10903
- return clone;
10904
11358
  };
10905
- var HierarchicalEdgeBundling_default = HierarchicalEdgeBundling;
10906
11359
 
10907
- // src/components/Tree/layout/RadialTree.ts
10908
- import { hierarchy as hierarchy2, linkRadial, select as select2, tree } from "d3";
10909
- var RadialTree = (s, data, options) => {
10910
- const svg = select2(s);
10911
- svg.selectAll("*").remove();
10912
- const { label, radius = 400, r = 4, slots } = options;
10913
- const arc = 2 * Math.PI;
10914
- const root = hierarchy2(data);
10915
- const descendants = root.descendants();
10916
- const getLabel = label === null ? null : descendants.map((d) => label(d.data));
10917
- const layout = tree().size([
10918
- arc,
11360
+ // src/components/Tree/layout/RadialTree.tsx
11361
+ import { cluster as d3Cluster, linkRadial, select as select3, tree as d3Tree } from "d3";
11362
+ import React7, { useCallback as useCallback3, useEffect as useEffect8, useMemo as useMemo4, useRef as useRef5, useState as useState4 } from "react";
11363
+ import { mx as mx2 } from "@dxos/ui-theme";
11364
+
11365
+ // src/components/Tree/layout/hierarchy.ts
11366
+ import { hierarchy as d3Hierarchy } from "d3";
11367
+ var buildHierarchy = (data, collapsed = /* @__PURE__ */ new Set()) => {
11368
+ return d3Hierarchy(data, (d) => {
11369
+ if (!d.children?.length) {
11370
+ return void 0;
11371
+ }
11372
+ return collapsed.has(d.id) ? void 0 : d.children;
11373
+ });
11374
+ };
11375
+ var isCollapsed = (data, collapsed) => Boolean(data.children?.length) && collapsed.has(data.id);
11376
+ var isLeaf = (data) => !data.children?.length;
11377
+
11378
+ // src/components/Tree/layout/RadialTree.tsx
11379
+ var TRANSITION_MS3 = 350;
11380
+ var RadialTree = ({ classNames, data, label = (d) => d.label ?? d.id, slots = defaultTreeLayoutSlots, r = 4, padding = 80, initialCollapsed, cluster: cluster2 = false, onNodeClick, onNodeHover }) => {
11381
+ const svgRef = useRef5(null);
11382
+ const { setRef, width, height } = useContainerSize();
11383
+ const [collapsed, setCollapsed] = useState4(() => new Set(initialCollapsed ?? []));
11384
+ const toggle = useCallback3((id) => {
11385
+ setCollapsed((prev) => {
11386
+ const next = new Set(prev);
11387
+ if (next.has(id)) {
11388
+ next.delete(id);
11389
+ } else {
11390
+ next.add(id);
11391
+ }
11392
+ return next;
11393
+ });
11394
+ }, []);
11395
+ const handleClickRef = useRef5(() => {
11396
+ });
11397
+ handleClickRef.current = (node) => {
11398
+ onNodeClick?.(node);
11399
+ if (node.children?.length) {
11400
+ toggle(node.id);
11401
+ }
11402
+ };
11403
+ const handleHoverRef = useRef5(() => {
11404
+ });
11405
+ handleHoverRef.current = (node, event) => onNodeHover?.(node, event);
11406
+ const root = useMemo4(() => buildHierarchy(data, collapsed), [
11407
+ data,
11408
+ collapsed
11409
+ ]);
11410
+ useEffect8(() => {
11411
+ if (!svgRef.current || !width || !height) {
11412
+ return;
11413
+ }
11414
+ const radius = Math.max(0, Math.min(width, height) / 2 - padding);
11415
+ renderRadialTree(svgRef.current, root, {
11416
+ radius,
11417
+ r,
11418
+ label,
11419
+ slots,
11420
+ collapsed,
11421
+ cluster: cluster2,
11422
+ onNodeClick: (n) => handleClickRef.current(n),
11423
+ onNodeHover: (n, e) => handleHoverRef.current(n, e)
11424
+ });
11425
+ }, [
11426
+ root,
11427
+ width,
11428
+ height,
11429
+ r,
11430
+ padding,
11431
+ label,
11432
+ slots,
11433
+ collapsed,
11434
+ cluster2
11435
+ ]);
11436
+ return /* @__PURE__ */ React7.createElement("div", {
11437
+ ref: setRef,
11438
+ className: mx2("dx-expander relative", classNames)
11439
+ }, width > 0 && height > 0 && /* @__PURE__ */ React7.createElement("svg", {
11440
+ ref: svgRef,
11441
+ xmlns: "http://www.w3.org/2000/svg",
11442
+ width,
11443
+ height,
11444
+ viewBox: `${-width / 2} ${-height / 2} ${width} ${height}`
11445
+ }));
11446
+ };
11447
+ var renderRadialTree = (svgElement, root, options) => {
11448
+ const { radius, r, label, slots, collapsed, cluster: cluster2, onNodeClick, onNodeHover } = options;
11449
+ const svg = select3(svgElement);
11450
+ const layout = cluster2 ? d3Cluster() : d3Tree();
11451
+ layout.size([
11452
+ 2 * Math.PI,
10919
11453
  radius
10920
- ]).separation((a, b) => (a.parent === b.parent ? 1 : 2) / a.depth);
10921
- layout(root);
10922
- svg.append("g").selectAll("path").data(root.links()).join("path").attr("class", slots?.path ?? "").attr("d", linkRadial().angle((d) => d.x + Math.PI / 2).radius((d) => d.y));
10923
- const node = svg.append("g").selectAll("a").data(root.descendants()).join("a").attr("transform", (d) => `rotate(${d.x * 180 / Math.PI}) translate(${d.y},0)`);
10924
- node.append("circle").attr("class", slots?.node ?? "").attr("r", r);
10925
- if (getLabel) {
10926
- node.append("text").attr("transform", (d) => `rotate(${d.x >= Math.PI ? 180 : 0})`).attr("dy", "0.32em").attr("x", (d) => d.x < Math.PI === !d.children ? 6 : -6).attr("text-anchor", (d) => d.x < Math.PI === !d.children ? "start" : "end").attr("class", slots?.text ?? "").text((d, i) => getLabel[i]);
10927
- }
10928
- return svg.node();
11454
+ ]).separation((a, b) => (a.parent === b.parent ? 1 : 2) / Math.max(1, a.depth))(root);
11455
+ const g = svg.selectAll("g.dx-radial-root").data([
11456
+ null
11457
+ ]).join("g").classed("dx-radial-root", true);
11458
+ const linksLayer = g.selectAll("g.dx-radial-links").data([
11459
+ null
11460
+ ]).join("g").classed("dx-radial-links", true);
11461
+ const nodesLayer = g.selectAll("g.dx-radial-nodes").data([
11462
+ null
11463
+ ]).join("g").classed("dx-radial-nodes", true);
11464
+ const linkPath = linkRadial().angle((d) => d.x).radius((d) => d.y);
11465
+ linksLayer.selectAll("path").data(root.links(), (d) => `${d.source.data.id}->${d.target.data.id}`).join((enter) => enter.append("path").attr("class", slots.path ?? "").attr("fill", "none").attr("opacity", 0), (update) => update, (exit) => exit.transition().duration(TRANSITION_MS3).attr("opacity", 0).remove()).transition().duration(TRANSITION_MS3).attr("opacity", 1).attr("d", linkPath);
11466
+ const node = nodesLayer.selectAll("g.dx-radial-node").data(root.descendants(), (d) => d.data.id);
11467
+ const nodeEnter = node.enter().append("g").classed("dx-radial-node", true).attr("opacity", 0).attr("transform", (d) => `rotate(${d.x * 180 / Math.PI - 90}) translate(${d.y},0)`).style("cursor", (d) => d.data.children?.length ? "pointer" : "default").on("click", (_, d) => onNodeClick(d.data));
11468
+ nodeEnter.append("circle").attr("r", r).on("pointerenter", (event, d) => onNodeHover(d.data, event)).on("pointerleave", (event) => onNodeHover(null, event));
11469
+ nodeEnter.append("text").attr("dy", "0.32em").attr("paint-order", "stroke").text((d) => label(d.data));
11470
+ const nodeMerge = nodeEnter.merge(node);
11471
+ nodeMerge.transition().duration(TRANSITION_MS3).attr("opacity", 1).attr("transform", (d) => `rotate(${d.x * 180 / Math.PI - 90}) translate(${d.y},0)`);
11472
+ nodeMerge.select("circle").attr("class", (d) => {
11473
+ const collapsedHere = isCollapsed(d.data, collapsed);
11474
+ const leaf = isLeaf(d.data);
11475
+ return [
11476
+ slots.node ?? "",
11477
+ collapsedHere ? "dx-collapsed" : leaf ? "dx-leaf" : "dx-branch"
11478
+ ].filter(Boolean).join(" ");
11479
+ }).attr("r", r).style("fill", (d) => isLeaf(d.data) ? getNodeFillForObject(d.data.data) : null);
11480
+ nodeMerge.select("text").attr("class", slots.text ?? "").attr("transform", (d) => d.x >= Math.PI ? "rotate(180)" : null).attr("x", (d) => d.x < Math.PI === !d.children ? r + 4 : -(r + 4)).attr("text-anchor", (d) => d.x < Math.PI === !d.children ? "start" : "end").text((d) => label(d.data));
11481
+ node.exit().transition().duration(TRANSITION_MS3).attr("opacity", 0).remove();
10929
11482
  };
10930
- var RadialTree_default = RadialTree;
10931
11483
 
10932
- // src/components/Tree/layout/TidyTree.ts
10933
- import { curveBumpX, hierarchy as hierarchy3, link, select as select3, tree as tree2 } from "d3";
10934
- var TidyTree = (s, data, options) => {
10935
- const svg = select3(s);
10936
- svg.selectAll("*").remove();
10937
- const { label, width, height, r = 4, padding = 4, margin = 60, slots } = options;
10938
- const root = hierarchy3(data);
10939
- const descendants = root.descendants();
10940
- const getLabel = label == null ? null : descendants.map((d) => label(d.data));
10941
- const dx = 16;
10942
- const dy = width / (root.height + padding);
10943
- const layout = tree2().nodeSize([
11484
+ // src/components/Tree/layout/TidyTree.tsx
11485
+ import { curveBumpX, link as d3Link, select as select4, tree as d3Tree2 } from "d3";
11486
+ import React8, { useCallback as useCallback4, useEffect as useEffect9, useMemo as useMemo5, useRef as useRef6, useState as useState5 } from "react";
11487
+ import { mx as mx3 } from "@dxos/ui-theme";
11488
+ var TRANSITION_MS4 = 350;
11489
+ var TidyTree = ({ classNames, data, label = (d) => d.label ?? d.id, slots = defaultTreeLayoutSlots, r = 4, margin = 24, initialCollapsed, onNodeClick }) => {
11490
+ const svgRef = useRef6(null);
11491
+ const { setRef, width, height } = useContainerSize();
11492
+ const [collapsed, setCollapsed] = useState5(() => new Set(initialCollapsed ?? []));
11493
+ const toggle = useCallback4((id) => {
11494
+ setCollapsed((prev) => {
11495
+ const next = new Set(prev);
11496
+ if (next.has(id)) {
11497
+ next.delete(id);
11498
+ } else {
11499
+ next.add(id);
11500
+ }
11501
+ return next;
11502
+ });
11503
+ }, []);
11504
+ const handleClickRef = useRef6(() => {
11505
+ });
11506
+ handleClickRef.current = (node) => {
11507
+ onNodeClick?.(node);
11508
+ if (node.children?.length) {
11509
+ toggle(node.id);
11510
+ }
11511
+ };
11512
+ const root = useMemo5(() => buildHierarchy(data, collapsed), [
11513
+ data,
11514
+ collapsed
11515
+ ]);
11516
+ useEffect9(() => {
11517
+ if (!svgRef.current || !width || !height) {
11518
+ return;
11519
+ }
11520
+ renderTidyTree(svgRef.current, root, {
11521
+ width,
11522
+ height,
11523
+ r,
11524
+ margin,
11525
+ label,
11526
+ slots,
11527
+ collapsed,
11528
+ onNodeClick: (n) => handleClickRef.current(n)
11529
+ });
11530
+ }, [
11531
+ root,
11532
+ width,
11533
+ height,
11534
+ r,
11535
+ margin,
11536
+ label,
11537
+ slots,
11538
+ collapsed
11539
+ ]);
11540
+ return /* @__PURE__ */ React8.createElement("div", {
11541
+ ref: setRef,
11542
+ className: mx3("dx-expander relative", classNames)
11543
+ }, width > 0 && height > 0 && /* @__PURE__ */ React8.createElement("svg", {
11544
+ ref: svgRef,
11545
+ xmlns: "http://www.w3.org/2000/svg",
11546
+ width,
11547
+ height,
11548
+ viewBox: `${-width / 2} ${-height / 2} ${width} ${height}`
11549
+ }));
11550
+ };
11551
+ var renderTidyTree = (svgElement, root, options) => {
11552
+ const { width, height, r, margin, label, slots, collapsed, onNodeClick } = options;
11553
+ const svg = select4(svgElement);
11554
+ const dx = 18;
11555
+ const dy = Math.max(60, (width - margin * 2) / Math.max(1, root.height + 1));
11556
+ d3Tree2().nodeSize([
10944
11557
  dx,
10945
11558
  dy
10946
- ]);
10947
- layout(root);
11559
+ ])(root);
10948
11560
  let x0 = Infinity;
10949
11561
  let x1 = -x0;
10950
- let y0 = Infinity;
10951
- let y1 = -y0;
10952
11562
  root.each((d) => {
10953
11563
  if (d.x > x1) {
10954
11564
  x1 = d.x;
@@ -10956,136 +11566,102 @@ var TidyTree = (s, data, options) => {
10956
11566
  if (d.x < x0) {
10957
11567
  x0 = d.x;
10958
11568
  }
10959
- if (d.y > y1) {
10960
- y1 = d.y;
10961
- }
10962
- if (d.y < y0) {
10963
- y0 = d.y;
10964
- }
10965
11569
  });
10966
- const sx = Math.min(2, Math.max(1, (height - margin * 2) / (x1 - x0)));
10967
- const oy = -(width - (y1 - y0)) / 2;
10968
- svg.append("g").selectAll("path").data(root.links()).join("path").attr("class", slots?.path ?? "").attr("d", link(curveBumpX).x((d) => d.y + oy).y((d) => d.x * sx));
10969
- const node = svg.append("g").selectAll("a").data(root.descendants()).join("a").attr("transform", (d) => `translate(${d.y + oy},${d.x * sx})`);
10970
- node.append("circle").attr("class", slots?.node ?? "").attr("r", r);
10971
- if (getLabel) {
10972
- node.append("text").attr("dy", "0.32em").attr("x", (d) => d.children ? -6 : 6).attr("text-anchor", (d) => d.children ? "end" : "start").attr("class", slots?.text ?? "").text((d, i) => getLabel[i]);
10973
- }
10974
- };
10975
- var TidyTree_default = TidyTree;
10976
-
10977
- // src/components/Tree/types/tree.ts
10978
- import { Schema } from "effect";
10979
- import { Key, Obj, Type } from "@dxos/echo";
10980
- import { invariant } from "@dxos/invariant";
10981
- var TreeNodeType = Schema.Struct({
10982
- id: Key.ObjectId,
10983
- children: Schema.mutable(Schema.Array(Key.ObjectId)),
10984
- data: Schema.mutable(Schema.Record({
10985
- key: Schema.String,
10986
- value: Schema.Any
10987
- })),
10988
- ref: Schema.optional(Type.Ref(Type.Expando))
10989
- }).pipe(Schema.mutable);
10990
- var TreeType = Schema.Struct({
10991
- root: Key.ObjectId,
10992
- nodes: Schema.mutable(Schema.Record({
10993
- key: Key.ObjectId,
10994
- value: TreeNodeType
10995
- }))
10996
- }).pipe(Type.Obj({
10997
- typename: "dxos.org/type/Tree",
10998
- version: "0.1.0"
10999
- }));
11000
-
11001
- // src/components/Tree/types/types.ts
11002
- var mapGraphToTreeData = (model, maxDepth = 8) => {
11003
- let data;
11004
- return data;
11570
+ const treeWidth = width - margin * 2;
11571
+ const treeHeight = x1 - x0;
11572
+ const scaleY = treeHeight > 0 ? Math.min(1, (height - margin * 2) / treeHeight) : 1;
11573
+ const offsetX = -treeWidth / 2;
11574
+ const offsetY = -(x0 + x1) / 2;
11575
+ const g = svg.selectAll("g.dx-tidy-root").data([
11576
+ null
11577
+ ]).join("g").classed("dx-tidy-root", true);
11578
+ const linksLayer = g.selectAll("g.dx-tidy-links").data([
11579
+ null
11580
+ ]).join("g").classed("dx-tidy-links", true);
11581
+ const nodesLayer = g.selectAll("g.dx-tidy-nodes").data([
11582
+ null
11583
+ ]).join("g").classed("dx-tidy-nodes", true);
11584
+ const linkPath = d3Link(curveBumpX).x((d) => offsetX + d.y).y((d) => (d.x + offsetY) * scaleY);
11585
+ linksLayer.selectAll("path").data(root.links(), (d) => `${d.source.data.id}->${d.target.data.id}`).join((enter) => enter.append("path").attr("class", slots.path ?? "").attr("fill", "none").attr("opacity", 0), (update) => update, (exit) => exit.transition().duration(TRANSITION_MS4).attr("opacity", 0).remove()).transition().duration(TRANSITION_MS4).attr("opacity", 1).attr("d", linkPath);
11586
+ const node = nodesLayer.selectAll("g.dx-tidy-node").data(root.descendants(), (d) => d.data.id);
11587
+ const nodeEnter = node.enter().append("g").classed("dx-tidy-node", true).attr("transform", (d) => `translate(${offsetX + d.y},${(d.x + offsetY) * scaleY})`).attr("opacity", 0).style("cursor", (d) => d.data.children?.length ? "pointer" : "default").on("click", (_, d) => onNodeClick(d.data));
11588
+ nodeEnter.append("circle").attr("r", r);
11589
+ nodeEnter.append("text").attr("dy", "0.32em").attr("x", (d) => d.children ? -(r + 4) : r + 4).attr("text-anchor", (d) => d.children ? "end" : "start").text((d) => label(d.data));
11590
+ const nodeMerge = nodeEnter.merge(node);
11591
+ nodeMerge.transition().duration(TRANSITION_MS4).attr("opacity", 1).attr("transform", (d) => `translate(${offsetX + d.y},${(d.x + offsetY) * scaleY})`);
11592
+ nodeMerge.select("circle").attr("class", (d) => {
11593
+ const collapsedHere = isCollapsed(d.data, collapsed);
11594
+ const leaf = isLeaf(d.data);
11595
+ return [
11596
+ slots.node ?? "",
11597
+ collapsedHere ? "dx-collapsed" : leaf ? "dx-leaf" : "dx-branch"
11598
+ ].filter(Boolean).join(" ");
11599
+ }).attr("r", r);
11600
+ nodeMerge.select("text").attr("class", slots.text ?? "").attr("x", (d) => d.children ? -(r + 4) : r + 4).attr("text-anchor", (d) => d.children ? "end" : "start").text((d) => label(d.data));
11601
+ node.exit().each(function() {
11602
+ select4(this).interrupt();
11603
+ }).transition().duration(TRANSITION_MS4).attr("opacity", 0).remove();
11005
11604
  };
11006
11605
 
11007
11606
  // src/components/Tree/Tree.tsx
11008
- var defaultTreeLayoutSlots = {
11009
- node: "fill-blue-600",
11010
- path: "fill-none stroke-blue-400 stroke-[0.5px]",
11011
- text: "stroke-[0.5px] stroke-neutral-700 text-xs"
11012
- };
11013
- var renderers = /* @__PURE__ */ new Map([
11014
- [
11015
- "tidy",
11016
- TidyTree_default
11017
- ],
11018
- [
11019
- "radial",
11020
- RadialTree_default
11021
- ],
11022
- [
11023
- "edge",
11024
- HierarchicalEdgeBundling_default
11025
- ]
11026
- ]);
11027
- var Tree = ({ space, selected, variant = "tidy", onNodeClick }) => {
11028
- var _effect = _useSignals3();
11029
- try {
11030
- const [model] = useAsyncState(async () => space ? new SpaceGraphModel().open(space) : void 0, [
11031
- space,
11032
- selected
11033
- ]);
11034
- const [tree3, setTree] = useState();
11035
- useEffect3(() => {
11036
- return model?.subscribe(() => {
11037
- const tree4 = mapGraphToTreeData(model);
11038
- setTree(tree4);
11039
- }, true);
11040
- }, [
11041
- model
11042
- ]);
11043
- const context = useRef(null);
11044
- useEffect3(() => {
11045
- if (context.current) {
11046
- const { width, height } = context.current.size;
11047
- const size = Math.min(width, height);
11048
- const radius = size * 0.4;
11049
- const options = {
11050
- // TODO(burdon): Type.
11051
- label: (d) => d.label ?? d.id,
11052
- width,
11053
- height,
11054
- radius,
11055
- marginLeft: (width - radius * 2) / 2,
11056
- marginRight: (width - radius * 2) / 2,
11057
- marginTop: (height - radius * 2) / 2,
11058
- marginBottom: (height - radius * 2) / 2,
11059
- slots: defaultTreeLayoutSlots
11060
- };
11061
- if (tree3) {
11062
- const renderer = renderers.get(variant);
11063
- renderer?.(context.current.svg, tree3, options);
11064
- }
11065
- }
11066
- }, [
11067
- context.current,
11068
- tree3
11069
- ]);
11070
- return /* @__PURE__ */ React3.createElement("div", {
11071
- onClick: () => onNodeClick?.()
11072
- }, /* @__PURE__ */ React3.createElement(SVG.Root, {
11073
- ref: context
11074
- }));
11075
- } finally {
11076
- _effect.f();
11077
- }
11607
+ var Tree = ({ classNames, data, edges, variant = "tidy", label, slots, initialCollapsed, onNodeClick, onNodeHover }) => {
11608
+ return useMemo6(() => {
11609
+ switch (variant) {
11610
+ case "tidy":
11611
+ return /* @__PURE__ */ React9.createElement(TidyTree, {
11612
+ classNames,
11613
+ data,
11614
+ label,
11615
+ slots,
11616
+ initialCollapsed,
11617
+ onNodeClick
11618
+ });
11619
+ case "radial":
11620
+ return /* @__PURE__ */ React9.createElement(RadialTree, {
11621
+ classNames,
11622
+ data,
11623
+ label,
11624
+ slots,
11625
+ initialCollapsed,
11626
+ onNodeClick,
11627
+ onNodeHover
11628
+ });
11629
+ case "edge":
11630
+ return /* @__PURE__ */ React9.createElement(HierarchicalEdgeBundling, {
11631
+ classNames,
11632
+ data,
11633
+ edges: edges ?? [],
11634
+ label,
11635
+ slots,
11636
+ onNodeHover
11637
+ });
11638
+ }
11639
+ }, [
11640
+ variant,
11641
+ classNames,
11642
+ data,
11643
+ edges,
11644
+ label,
11645
+ slots,
11646
+ initialCollapsed,
11647
+ onNodeClick,
11648
+ onNodeHover
11649
+ ]);
11078
11650
  };
11079
-
11080
- // src/components/index.ts
11081
- import { lazy } from "react";
11082
- var ExplorerContainer = lazy(() => import("./ExplorerContainer-AMYAVLO4.mjs"));
11083
-
11084
11651
  export {
11652
+ CanvasForceGraph,
11085
11653
  Chart,
11654
+ ForceGraph,
11086
11655
  Globe,
11087
- defaultTreeLayoutSlots,
11656
+ HierarchicalEdgeBundling,
11657
+ Lattice,
11658
+ RadialTree,
11659
+ TidyTree,
11088
11660
  Tree,
11089
- ExplorerContainer
11661
+ buildHierarchy,
11662
+ defaultTreeLayoutSlots,
11663
+ isCollapsed,
11664
+ isLeaf,
11665
+ treeTypeToTreeNode
11090
11666
  };
11091
- //# sourceMappingURL=chunk-W4ZNCGOD.mjs.map
11667
+ //# sourceMappingURL=index.mjs.map