@dxos/plugin-explorer 0.8.4-main.406dc2a → 0.8.4-main.43cb759274

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 (270) 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-HI324IB4.mjs +69 -0
  15. package/dist/lib/neutral/chunk-HI324IB4.mjs.map +7 -0
  16. package/dist/lib/neutral/chunk-IKHJV3Q4.mjs +20 -0
  17. package/dist/lib/neutral/chunk-IKHJV3Q4.mjs.map +7 -0
  18. package/dist/lib/{browser/chunk-MOM5KCKC.mjs → neutral/components/index.mjs} +882 -556
  19. package/dist/lib/neutral/components/index.mjs.map +7 -0
  20. package/dist/lib/neutral/containers/index.mjs +9 -0
  21. package/dist/lib/neutral/containers/index.mjs.map +7 -0
  22. package/dist/lib/neutral/create-object-F6TKVAGV.mjs +39 -0
  23. package/dist/lib/neutral/create-object-F6TKVAGV.mjs.map +7 -0
  24. package/dist/lib/neutral/hooks/index.mjs +45 -0
  25. package/dist/lib/neutral/hooks/index.mjs.map +7 -0
  26. package/dist/lib/neutral/index.mjs +14 -0
  27. package/dist/lib/neutral/meta.json +1 -0
  28. package/dist/lib/{browser → neutral}/meta.mjs +1 -1
  29. package/dist/lib/neutral/plugin.mjs +12 -0
  30. package/dist/lib/neutral/plugin.mjs.map +7 -0
  31. package/dist/lib/neutral/react-surface-APBW2VQG.mjs +26 -0
  32. package/dist/lib/neutral/react-surface-APBW2VQG.mjs.map +7 -0
  33. package/dist/lib/neutral/testing/index.mjs +139 -0
  34. package/dist/lib/neutral/testing/index.mjs.map +7 -0
  35. package/dist/lib/neutral/translations.mjs +33 -0
  36. package/dist/lib/neutral/translations.mjs.map +7 -0
  37. package/dist/lib/{browser → neutral}/types/index.mjs +1 -2
  38. package/dist/types/data/cities.d.ts +4 -4
  39. package/dist/types/data/cities.d.ts.map +1 -1
  40. package/dist/types/data/countries-110m.d.ts +19 -22
  41. package/dist/types/data/countries-110m.d.ts.map +1 -1
  42. package/dist/types/src/ExplorerPlugin.d.ts +3 -1
  43. package/dist/types/src/ExplorerPlugin.d.ts.map +1 -1
  44. package/dist/types/src/ExplorerPlugin.test.d.ts +2 -0
  45. package/dist/types/src/ExplorerPlugin.test.d.ts.map +1 -0
  46. package/dist/types/src/capabilities/create-object.d.ts +11 -0
  47. package/dist/types/src/capabilities/create-object.d.ts.map +1 -0
  48. package/dist/types/src/capabilities/index.d.ts +8 -2
  49. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  50. package/dist/types/src/capabilities/react-surface.d.ts +3 -2
  51. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  52. package/dist/types/src/components/Chart/Chart.d.ts +1 -1
  53. package/dist/types/src/components/Chart/Chart.d.ts.map +1 -1
  54. package/dist/types/src/components/Chart/Chart.stories.d.ts +4 -1
  55. package/dist/types/src/components/Chart/Chart.stories.d.ts.map +1 -1
  56. package/dist/types/src/components/Globe/Globe.d.ts +1 -1
  57. package/dist/types/src/components/Globe/Globe.d.ts.map +1 -1
  58. package/dist/types/src/components/Globe/Globe.stories.d.ts +5 -2
  59. package/dist/types/src/components/Globe/Globe.stories.d.ts.map +1 -1
  60. package/dist/types/src/components/Graph/CanvasForceGraph.d.ts +13 -0
  61. package/dist/types/src/components/Graph/CanvasForceGraph.d.ts.map +1 -0
  62. package/dist/types/src/components/Graph/CanvasForceGraph.stories.d.ts +17 -0
  63. package/dist/types/src/components/Graph/CanvasForceGraph.stories.d.ts.map +1 -0
  64. package/dist/types/src/components/Graph/ForceGraph.d.ts +12 -5
  65. package/dist/types/src/components/Graph/ForceGraph.d.ts.map +1 -1
  66. package/dist/types/src/components/Graph/ForceGraph.stories.d.ts +4 -2
  67. package/dist/types/src/components/Graph/ForceGraph.stories.d.ts.map +1 -1
  68. package/dist/types/src/components/Graph/{adapter.d.ts → graph-adapter.d.ts} +2 -2
  69. package/dist/types/src/components/Graph/graph-adapter.d.ts.map +1 -0
  70. package/dist/types/src/components/Graph/index.d.ts +1 -1
  71. package/dist/types/src/components/Graph/index.d.ts.map +1 -1
  72. package/dist/types/src/components/Lattice/Lattice.d.ts +20 -0
  73. package/dist/types/src/components/Lattice/Lattice.d.ts.map +1 -0
  74. package/dist/types/src/components/Lattice/Lattice.stories.d.ts +8 -0
  75. package/dist/types/src/components/Lattice/Lattice.stories.d.ts.map +1 -0
  76. package/dist/types/src/components/Lattice/index.d.ts +2 -0
  77. package/dist/types/src/components/Lattice/index.d.ts.map +1 -0
  78. package/dist/types/src/components/Tree/EdgeBundling.stories.d.ts +21 -0
  79. package/dist/types/src/components/Tree/EdgeBundling.stories.d.ts.map +1 -0
  80. package/dist/types/src/components/Tree/Tree.d.ts +20 -23
  81. package/dist/types/src/components/Tree/Tree.d.ts.map +1 -1
  82. package/dist/types/src/components/Tree/Tree.stories.d.ts +5 -12
  83. package/dist/types/src/components/Tree/Tree.stories.d.ts.map +1 -1
  84. package/dist/types/src/components/Tree/index.d.ts +2 -0
  85. package/dist/types/src/components/Tree/index.d.ts.map +1 -1
  86. package/dist/types/src/components/Tree/layout/HierarchicalEdgeBundling.d.ts +37 -2
  87. package/dist/types/src/components/Tree/layout/HierarchicalEdgeBundling.d.ts.map +1 -1
  88. package/dist/types/src/components/Tree/layout/RadialTree.d.ts +35 -2
  89. package/dist/types/src/components/Tree/layout/RadialTree.d.ts.map +1 -1
  90. package/dist/types/src/components/Tree/layout/TidyTree.d.ts +24 -2
  91. package/dist/types/src/components/Tree/layout/TidyTree.d.ts.map +1 -1
  92. package/dist/types/src/components/Tree/layout/hierarchy.d.ts +17 -0
  93. package/dist/types/src/components/Tree/layout/hierarchy.d.ts.map +1 -0
  94. package/dist/types/src/components/Tree/layout/index.d.ts +5 -4
  95. package/dist/types/src/components/Tree/layout/index.d.ts.map +1 -1
  96. package/dist/types/src/components/Tree/layout/slots.d.ts +7 -0
  97. package/dist/types/src/components/Tree/layout/slots.d.ts.map +1 -0
  98. package/dist/types/src/components/Tree/layout/useContainerSize.d.ts +15 -0
  99. package/dist/types/src/components/Tree/layout/useContainerSize.d.ts.map +1 -0
  100. package/dist/types/src/components/Tree/types/tree.d.ts +50 -27
  101. package/dist/types/src/components/Tree/types/tree.d.ts.map +1 -1
  102. package/dist/types/src/components/Tree/types/types.d.ts +14 -4
  103. package/dist/types/src/components/Tree/types/types.d.ts.map +1 -1
  104. package/dist/types/src/components/index.d.ts +1 -4
  105. package/dist/types/src/components/index.d.ts.map +1 -1
  106. package/dist/types/src/containers/ExplorerArticle/ExplorerArticle.d.ts +8 -0
  107. package/dist/types/src/containers/ExplorerArticle/ExplorerArticle.d.ts.map +1 -0
  108. package/dist/types/src/containers/ExplorerArticle/ExplorerArticle.stories.d.ts +15 -0
  109. package/dist/types/src/containers/ExplorerArticle/ExplorerArticle.stories.d.ts.map +1 -0
  110. package/dist/types/src/containers/ExplorerArticle/Visualization.d.ts +18 -0
  111. package/dist/types/src/containers/ExplorerArticle/Visualization.d.ts.map +1 -0
  112. package/dist/types/src/containers/ExplorerArticle/index.d.ts +2 -0
  113. package/dist/types/src/containers/ExplorerArticle/index.d.ts.map +1 -0
  114. package/dist/types/src/containers/ExplorerArticle/variants.d.ts +9 -0
  115. package/dist/types/src/containers/ExplorerArticle/variants.d.ts.map +1 -0
  116. package/dist/types/src/containers/index.d.ts +3 -0
  117. package/dist/types/src/containers/index.d.ts.map +1 -0
  118. package/dist/types/src/hooks/useGraphModel.d.ts +2 -2
  119. package/dist/types/src/hooks/useGraphModel.d.ts.map +1 -1
  120. package/dist/types/src/index.d.ts +1 -3
  121. package/dist/types/src/index.d.ts.map +1 -1
  122. package/dist/types/src/meta.d.ts +2 -2
  123. package/dist/types/src/meta.d.ts.map +1 -1
  124. package/dist/types/src/plugin.d.ts +3 -0
  125. package/dist/types/src/plugin.d.ts.map +1 -0
  126. package/dist/types/src/{components/Tree/testing → testing}/generator.d.ts +1 -1
  127. package/dist/types/src/testing/generator.d.ts.map +1 -0
  128. package/dist/types/src/testing/index.d.ts +4 -0
  129. package/dist/types/src/testing/index.d.ts.map +1 -0
  130. package/dist/types/src/testing/relations.d.ts +32 -0
  131. package/dist/types/src/testing/relations.d.ts.map +1 -0
  132. package/dist/types/src/translations.d.ts +31 -22
  133. package/dist/types/src/translations.d.ts.map +1 -1
  134. package/dist/types/src/types/ExplorerAction.d.ts +1 -18
  135. package/dist/types/src/types/ExplorerAction.d.ts.map +1 -1
  136. package/dist/types/src/types/Graph.d.ts +14 -25
  137. package/dist/types/src/types/Graph.d.ts.map +1 -1
  138. package/dist/types/src/util/index.d.ts +3 -0
  139. package/dist/types/src/util/index.d.ts.map +1 -0
  140. package/dist/types/src/util/node-color.d.ts +13 -0
  141. package/dist/types/src/util/node-color.d.ts.map +1 -0
  142. package/dist/types/src/{components → util}/plot.d.ts +1 -1
  143. package/dist/types/src/util/plot.d.ts.map +1 -0
  144. package/dist/types/tsconfig.tsbuildinfo +1 -1
  145. package/package.json +113 -65
  146. package/src/ExplorerPlugin.test.ts +26 -0
  147. package/src/ExplorerPlugin.tsx +20 -54
  148. package/src/capabilities/create-object.ts +36 -0
  149. package/src/capabilities/index.ts +3 -3
  150. package/src/capabilities/react-surface.tsx +24 -19
  151. package/src/components/Chart/Chart.stories.tsx +16 -23
  152. package/src/components/Chart/Chart.tsx +1 -1
  153. package/src/components/Globe/Globe.stories.tsx +19 -22
  154. package/src/components/Globe/Globe.tsx +1 -1
  155. package/src/components/Graph/CanvasForceGraph.stories.tsx +97 -0
  156. package/src/components/Graph/CanvasForceGraph.tsx +124 -0
  157. package/src/components/Graph/ForceGraph.stories.tsx +98 -42
  158. package/src/components/Graph/ForceGraph.tsx +105 -85
  159. package/src/components/Graph/{adapter.ts → graph-adapter.ts} +14 -8
  160. package/src/components/Graph/index.ts +1 -1
  161. package/src/components/Lattice/Lattice.stories.tsx +104 -0
  162. package/src/components/Lattice/Lattice.tsx +182 -0
  163. package/src/components/Lattice/index.ts +5 -0
  164. package/src/components/Tree/EdgeBundling.stories.tsx +144 -0
  165. package/src/components/Tree/Tree.stories.tsx +20 -38
  166. package/src/components/Tree/Tree.tsx +69 -95
  167. package/src/components/Tree/index.ts +2 -0
  168. package/src/components/Tree/layout/HierarchicalEdgeBundling.tsx +335 -0
  169. package/src/components/Tree/layout/RadialTree.tsx +242 -0
  170. package/src/components/Tree/layout/TidyTree.tsx +246 -0
  171. package/src/components/Tree/layout/hierarchy.ts +32 -0
  172. package/src/components/Tree/layout/index.ts +5 -5
  173. package/src/components/Tree/layout/slots.ts +19 -0
  174. package/src/components/Tree/layout/useContainerSize.ts +43 -0
  175. package/src/components/Tree/types/tree.test.ts +8 -7
  176. package/src/components/Tree/types/tree.ts +51 -35
  177. package/src/components/Tree/types/types.ts +38 -29
  178. package/src/components/index.ts +1 -4
  179. package/src/containers/ExplorerArticle/ExplorerArticle.stories.tsx +152 -0
  180. package/src/containers/ExplorerArticle/ExplorerArticle.tsx +120 -0
  181. package/src/containers/ExplorerArticle/Visualization.tsx +523 -0
  182. package/src/containers/ExplorerArticle/index.ts +5 -0
  183. package/src/containers/ExplorerArticle/variants.ts +47 -0
  184. package/src/containers/index.ts +7 -0
  185. package/src/hooks/useGraphModel.ts +25 -14
  186. package/src/index.ts +1 -4
  187. package/src/meta.ts +30 -6
  188. package/src/plugin.ts +9 -0
  189. package/src/{components/Tree/testing → testing}/generator.ts +6 -4
  190. package/src/testing/index.ts +9 -0
  191. package/src/testing/relations.ts +117 -0
  192. package/src/translations.ts +16 -13
  193. package/src/types/ExplorerAction.ts +10 -19
  194. package/src/types/Graph.ts +20 -24
  195. package/src/typings.d.ts +8 -0
  196. package/src/util/index.ts +6 -0
  197. package/src/util/node-color.ts +23 -0
  198. package/src/{components → util}/plot.ts +16 -4
  199. package/src/vite-env.d.ts +10 -0
  200. package/dist/lib/browser/ExplorerContainer-6SHZJ6AK.mjs +0 -50
  201. package/dist/lib/browser/ExplorerContainer-6SHZJ6AK.mjs.map +0 -7
  202. package/dist/lib/browser/chunk-2DGFNLRO.mjs +0 -19
  203. package/dist/lib/browser/chunk-2DGFNLRO.mjs.map +0 -7
  204. package/dist/lib/browser/chunk-2MKBRIUT.mjs +0 -31
  205. package/dist/lib/browser/chunk-2MKBRIUT.mjs.map +0 -7
  206. package/dist/lib/browser/chunk-BZ65T5M3.mjs +0 -79
  207. package/dist/lib/browser/chunk-BZ65T5M3.mjs.map +0 -7
  208. package/dist/lib/browser/chunk-MOM5KCKC.mjs.map +0 -7
  209. package/dist/lib/browser/chunk-NXGP6NTP.mjs +0 -203
  210. package/dist/lib/browser/chunk-NXGP6NTP.mjs.map +0 -7
  211. package/dist/lib/browser/index.mjs +0 -118
  212. package/dist/lib/browser/index.mjs.map +0 -7
  213. package/dist/lib/browser/intent-resolver-K57C3LIX.mjs +0 -31
  214. package/dist/lib/browser/intent-resolver-K57C3LIX.mjs.map +0 -7
  215. package/dist/lib/browser/meta.json +0 -1
  216. package/dist/lib/browser/react-surface-OPBND5W3.mjs +0 -35
  217. package/dist/lib/browser/react-surface-OPBND5W3.mjs.map +0 -7
  218. package/dist/lib/node-esm/ExplorerContainer-FRTDXZI5.mjs +0 -51
  219. package/dist/lib/node-esm/ExplorerContainer-FRTDXZI5.mjs.map +0 -7
  220. package/dist/lib/node-esm/chunk-3ODK27PU.mjs +0 -33
  221. package/dist/lib/node-esm/chunk-3ODK27PU.mjs.map +0 -7
  222. package/dist/lib/node-esm/chunk-6JACZE7E.mjs +0 -205
  223. package/dist/lib/node-esm/chunk-6JACZE7E.mjs.map +0 -7
  224. package/dist/lib/node-esm/chunk-ES6AOMCY.mjs +0 -80
  225. package/dist/lib/node-esm/chunk-ES6AOMCY.mjs.map +0 -7
  226. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +0 -11
  227. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs.map +0 -7
  228. package/dist/lib/node-esm/chunk-PX6LHR2N.mjs +0 -21
  229. package/dist/lib/node-esm/chunk-PX6LHR2N.mjs.map +0 -7
  230. package/dist/lib/node-esm/chunk-ZCV4U7LT.mjs +0 -11343
  231. package/dist/lib/node-esm/chunk-ZCV4U7LT.mjs.map +0 -7
  232. package/dist/lib/node-esm/index.mjs +0 -119
  233. package/dist/lib/node-esm/index.mjs.map +0 -7
  234. package/dist/lib/node-esm/intent-resolver-BLPPTTEY.mjs +0 -32
  235. package/dist/lib/node-esm/intent-resolver-BLPPTTEY.mjs.map +0 -7
  236. package/dist/lib/node-esm/meta.json +0 -1
  237. package/dist/lib/node-esm/meta.mjs +0 -9
  238. package/dist/lib/node-esm/react-surface-HCKQSHKJ.mjs +0 -36
  239. package/dist/lib/node-esm/react-surface-HCKQSHKJ.mjs.map +0 -7
  240. package/dist/lib/node-esm/types/index.mjs +0 -12
  241. package/dist/types/src/capabilities/intent-resolver.d.ts +0 -4
  242. package/dist/types/src/capabilities/intent-resolver.d.ts.map +0 -1
  243. package/dist/types/src/components/ExplorerContainer.d.ts +0 -9
  244. package/dist/types/src/components/ExplorerContainer.d.ts.map +0 -1
  245. package/dist/types/src/components/Graph/D3ForceGraph.d.ts +0 -14
  246. package/dist/types/src/components/Graph/D3ForceGraph.d.ts.map +0 -1
  247. package/dist/types/src/components/Graph/D3ForceGraph.stories.d.ts +0 -15
  248. package/dist/types/src/components/Graph/D3ForceGraph.stories.d.ts.map +0 -1
  249. package/dist/types/src/components/Graph/adapter.d.ts.map +0 -1
  250. package/dist/types/src/components/Graph/testing.d.ts +0 -14
  251. package/dist/types/src/components/Graph/testing.d.ts.map +0 -1
  252. package/dist/types/src/components/Tree/testing/generator.d.ts.map +0 -1
  253. package/dist/types/src/components/Tree/testing/index.d.ts +0 -2
  254. package/dist/types/src/components/Tree/testing/index.d.ts.map +0 -1
  255. package/dist/types/src/components/plot.d.ts.map +0 -1
  256. package/src/capabilities/intent-resolver.ts +0 -21
  257. package/src/components/ExplorerContainer.tsx +0 -54
  258. package/src/components/Graph/D3ForceGraph.stories.tsx +0 -77
  259. package/src/components/Graph/D3ForceGraph.tsx +0 -101
  260. package/src/components/Graph/testing.ts +0 -55
  261. package/src/components/Tree/layout/HierarchicalEdgeBundling.ts +0 -162
  262. package/src/components/Tree/layout/RadialTree.ts +0 -94
  263. package/src/components/Tree/layout/TidyTree.ts +0 -101
  264. package/src/components/Tree/testing/index.ts +0 -5
  265. /package/dist/lib/{browser/chunk-J5LGTIGS.mjs.map → neutral/ExplorerPlugin.mjs.map} +0 -0
  266. /package/dist/lib/{browser → neutral}/chunk-J5LGTIGS.mjs +0 -0
  267. /package/dist/lib/{browser/meta.mjs.map → neutral/chunk-J5LGTIGS.mjs.map} +0 -0
  268. /package/dist/lib/{browser/types → neutral}/index.mjs.map +0 -0
  269. /package/dist/lib/{node-esm → neutral}/meta.mjs.map +0 -0
  270. /package/dist/lib/{node-esm → neutral}/types/index.mjs.map +0 -0
@@ -1,15 +1,16 @@
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";
9
+
1
10
  // src/components/Chart/Chart.tsx
2
- import { useSignals as _useSignals } from "@preact-signals/safe-react/tracking";
3
11
  import * as Plot from "@observablehq/plot";
4
12
  import React, { useEffect } from "react";
5
13
  import { useResizeDetector } from "react-resize-detector";
6
-
7
- // src/components/plot.ts
8
- var createAdapter = (prop, accessor) => accessor ? {
9
- transform: (values) => values.map((value) => accessor(value)[prop])
10
- } : prop;
11
-
12
- // src/components/Chart/Chart.tsx
13
14
  var defaultOptions = {
14
15
  r: 4,
15
16
  stroke: "gray",
@@ -17,49 +18,43 @@ var defaultOptions = {
17
18
  fillOpacity: 0.2
18
19
  };
19
20
  var Chart = ({ items = [], accessor, options = defaultOptions }) => {
20
- var _effect = _useSignals();
21
- try {
22
- const { ref: containerRef, width = 0, height = 0 } = useResizeDetector({
23
- refreshRate: 200
24
- });
25
- useEffect(() => {
26
- if (!width || !height) {
27
- return;
28
- }
29
- const plot3 = Plot.plot({
30
- grid: true,
31
- width,
32
- height,
33
- style: {
34
- background: "transparent"
35
- },
36
- marks: [
37
- Plot.frame(),
38
- Plot.dot(items, {
39
- x: createAdapter("x", accessor),
40
- y: createAdapter("y", accessor),
41
- ...options
42
- })
43
- ]
44
- });
45
- containerRef.current.append(plot3);
46
- return () => plot3?.remove();
47
- }, [
48
- 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,
49
30
  width,
50
- height
51
- ]);
52
- return /* @__PURE__ */ React.createElement("div", {
53
- ref: containerRef,
54
- 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
+ ]
55
43
  });
56
- } finally {
57
- _effect.f();
58
- }
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
+ });
59
55
  };
60
56
 
61
57
  // src/components/Globe/Globe.tsx
62
- import { useSignals as _useSignals2 } from "@preact-signals/safe-react/tracking";
63
58
  import * as Plot2 from "@observablehq/plot";
64
59
  import React2, { useEffect as useEffect2 } from "react";
65
60
  import { useResizeDetector as useResizeDetector2 } from "react-resize-detector";
@@ -10773,569 +10768,900 @@ var defaultOptions2 = {
10773
10768
  fill: "#003300"
10774
10769
  };
10775
10770
  var Globe = ({ items = [], accessor, projection = "orthographic", options = defaultOptions2 }) => {
10776
- var _effect = _useSignals2();
10777
- try {
10778
- const { ref: containerRef, width = 0, height = 0 } = useResizeDetector2({
10779
- refreshRate: 200
10780
- });
10781
- const land = topojson.feature(countries_110m_default, countries_110m_default.objects.land);
10782
- useEffect2(() => {
10783
- if (!width || !height) {
10784
- return;
10785
- }
10786
- const plot3 = Plot2.plot({
10787
- // https://observablehq.com/plot/features/projections
10788
- projection: {
10789
- type: projection,
10790
- rotate: [
10791
- -100,
10792
- -20
10793
- ]
10794
- },
10795
- // projection: { type: 'equirectangular', rotate: [-140, -30] },
10796
- width,
10797
- height,
10798
- style: {
10799
- background: "transparent"
10800
- },
10801
- // TODO(burdon): Create simple wrapper for Plot with good defaults.
10802
- marks: [
10803
- Plot2.sphere({
10804
- fill: "lightblue",
10805
- fillOpacity: 0.5
10806
- }),
10807
- Plot2.geo(land, {
10808
- fill: "darkgreen",
10809
- fillOpacity: 0.5
10810
- }),
10811
- Plot2.graticule(),
10812
- Plot2.dot(items, {
10813
- x: createAdapter("lat", accessor),
10814
- y: createAdapter("lng", accessor),
10815
- ...options
10816
- })
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
10817
10786
  ]
10818
- });
10819
- containerRef.current.append(plot3);
10820
- return () => plot3?.remove();
10821
- }, [
10822
- items,
10787
+ },
10788
+ // projection: { type: 'equirectangular', rotate: [-140, -30] },
10823
10789
  width,
10824
- height
10825
- ]);
10826
- return /* @__PURE__ */ React2.createElement("div", {
10827
- ref: containerRef,
10828
- className: "grow p-4"
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
+ ]
10829
10811
  });
10830
- } finally {
10831
- _effect.f();
10832
- }
10833
- };
10834
-
10835
- // src/components/Tree/Tree.tsx
10836
- import { useSignals as _useSignals3 } from "@preact-signals/safe-react/tracking";
10837
- import React3, { useEffect as useEffect3, useRef, useState } from "react";
10838
- import { useAsyncState } from "@dxos/react-ui";
10839
- import { SVG } from "@dxos/react-ui-graph";
10840
- import { SpaceGraphModel } from "@dxos/schema";
10841
-
10842
- // src/components/Tree/layout/HierarchicalEdgeBundling.ts
10843
- import { cluster, curveBundle, hierarchy, lineRadial, select } from "d3";
10844
- var HierarchicalEdgeBundling = (s, data, options) => {
10845
- const svg = select(s);
10846
- svg.selectAll("*").remove();
10847
- const { radius = 600, padding = 100, slots } = options;
10848
- const root = hierarchy(flatten(data));
10849
- const tree3 = cluster().size([
10850
- 2 * Math.PI,
10851
- radius - padding
10812
+ containerRef.current.append(plot3);
10813
+ return () => plot3?.remove();
10814
+ }, [
10815
+ items,
10816
+ width,
10817
+ height
10852
10818
  ]);
10853
- const layout = tree3(addLinks(root));
10854
- 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)));
10855
- const line = lineRadial().curve(curveBundle.beta(0.85)).radius((d) => d.y).angle((d) => d.x);
10856
- 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]) => {
10857
- return line(i.path(o));
10858
- }).each(function(d) {
10859
- d.path = this;
10819
+ return /* @__PURE__ */ React2.createElement("div", {
10820
+ ref: containerRef,
10821
+ className: "grow p-4"
10860
10822
  });
10861
10823
  };
10862
- var addLinks = (root) => {
10863
- const nodes = new Map(root.descendants().map((d) => [
10864
- d.data.id,
10865
- d
10866
- ]));
10867
- const parents = root.descendants().reduce((map, d) => {
10868
- if (d.children?.length) {
10869
- map.set(d.data.id, d);
10870
- }
10871
- return map;
10872
- }, /* @__PURE__ */ new Map());
10873
- for (const d of root.leaves()) {
10874
- const parent = parents.get(d.data.id);
10875
- if (parent) {
10876
- d.outgoing = parent.data.children?.slice(1).map((child) => {
10877
- return [
10878
- d,
10879
- nodes.get(child.id)
10880
- ];
10881
- }) ?? [];
10882
- } else {
10883
- d.outgoing = [];
10884
- }
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
+ }));
10885
10851
  }
10886
- return root;
10887
- };
10888
- var flatten = (node) => {
10889
- const clone = {
10890
- id: node.id
10891
- };
10892
- if (node.children?.length) {
10893
- const children = node.children.map((child) => flatten(child));
10894
- clone.children = [
10895
- {
10896
- id: node.id
10897
- },
10898
- ...children
10899
- ];
10852
+ get nodes() {
10853
+ return this._nodes;
10900
10854
  }
10901
- return clone;
10902
- };
10903
- var HierarchicalEdgeBundling_default = HierarchicalEdgeBundling;
10904
-
10905
- // src/components/Tree/layout/RadialTree.ts
10906
- import { hierarchy as hierarchy2, linkRadial, select as select2, tree } from "d3";
10907
- var RadialTree = (s, data, options) => {
10908
- const svg = select2(s);
10909
- svg.selectAll("*").remove();
10910
- const { label, radius = 400, r = 4, slots } = options;
10911
- const arc = 2 * Math.PI;
10912
- const root = hierarchy2(data);
10913
- const descendants = root.descendants();
10914
- const getLabel = label === null ? null : descendants.map((d) => label(d.data));
10915
- const layout = tree().size([
10916
- arc,
10917
- radius
10918
- ]).separation((a, b) => (a.parent === b.parent ? 1 : 2) / a.depth);
10919
- layout(root);
10920
- 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));
10921
- 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)`);
10922
- node.append("circle").attr("class", slots?.node ?? "").attr("r", r);
10923
- if (getLabel) {
10924
- 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]);
10855
+ get links() {
10856
+ return this._links;
10925
10857
  }
10926
- return svg.node();
10927
10858
  };
10928
- var RadialTree_default = RadialTree;
10929
10859
 
10930
- // src/components/Tree/layout/TidyTree.ts
10931
- import { curveBumpX, hierarchy as hierarchy3, link, select as select3, tree as tree2 } from "d3";
10932
- var TidyTree = (s, data, options) => {
10933
- const svg = select3(s);
10934
- svg.selectAll("*").remove();
10935
- const { label, width, height, r = 4, padding = 4, margin = 60, slots } = options;
10936
- const root = hierarchy3(data);
10937
- const descendants = root.descendants();
10938
- const getLabel = label == null ? null : descendants.map((d) => label(d.data));
10939
- const dx = 16;
10940
- const dy = width / (root.height + padding);
10941
- const layout = tree2().nodeSize([
10942
- dx,
10943
- dy
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
10944
10871
  ]);
10945
- layout(root);
10946
- let x0 = Infinity;
10947
- let x1 = -x0;
10948
- let y0 = Infinity;
10949
- let y1 = -y0;
10950
- root.each((d) => {
10951
- if (d.x > x1) {
10952
- x1 = d.x;
10953
- }
10954
- if (d.x < x0) {
10955
- x0 = d.x;
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);
10956
10883
  }
10957
- if (d.y > y1) {
10958
- y1 = d.y;
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;
10959
10895
  }
10960
- if (d.y < y0) {
10961
- y0 = d.y;
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();
10962
10911
  }
10963
- });
10964
- const sx = Math.min(2, Math.max(1, (height - margin * 2) / (x1 - x0)));
10965
- const oy = -(width - (y1 - y0)) / 2;
10966
- 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));
10967
- const node = svg.append("g").selectAll("a").data(root.descendants()).join("a").attr("transform", (d) => `translate(${d.y + oy},${d.x * sx})`);
10968
- node.append("circle").attr("class", slots?.node ?? "").attr("r", r);
10969
- if (getLabel) {
10970
- 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]);
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;
10971
10931
  }
10972
10932
  };
10973
- var TidyTree_default = TidyTree;
10974
10933
 
10975
- // src/components/Tree/types/tree.ts
10976
- import * as Schema from "effect/Schema";
10977
- import { Key, Obj, Type } from "@dxos/echo";
10978
- import { invariant } from "@dxos/invariant";
10979
- function _define_property(obj, key, value) {
10980
- if (key in obj) {
10981
- Object.defineProperty(obj, key, {
10982
- value,
10983
- enumerable: true,
10984
- configurable: true,
10985
- writable: true
10986
- });
10987
- } else {
10988
- obj[key] = value;
10989
- }
10990
- return obj;
10991
- }
10992
- var __dxlog_file = "/__w/dxos/dxos/packages/plugins/plugin-explorer/src/components/Tree/types/tree.ts";
10993
- var TreeNodeType = Schema.Struct({
10994
- id: Key.ObjectId,
10995
- children: Schema.mutable(Schema.Array(Key.ObjectId)),
10996
- data: Schema.mutable(Schema.Record({
10997
- key: Schema.String,
10998
- value: Schema.Any
10999
- })),
11000
- ref: Schema.optional(Type.Ref(Type.Expando))
11001
- }).pipe(Schema.mutable);
11002
- var TreeType = Schema.Struct({
11003
- root: Key.ObjectId,
11004
- nodes: Schema.mutable(Schema.Record({
11005
- key: Key.ObjectId,
11006
- value: TreeNodeType
11007
- }))
11008
- }).pipe(Type.Obj({
11009
- typename: "dxos.org/type/Tree",
11010
- version: "0.1.0"
11011
- }));
11012
- var Tree = class _Tree {
11013
- get tree() {
11014
- return this._tree;
11015
- }
11016
- // TODO(burdon): Make reactive.
11017
- get size() {
11018
- return Object.keys(this._tree.nodes).length;
11019
- }
11020
- get root() {
11021
- return this.getNode(this._tree.root);
11022
- }
11023
- //
11024
- // Traversal
11025
- //
11026
- /**
11027
- * Recursively traverse the tree until the callback returns a value.
11028
- */
11029
- tranverse(callback, root = this._tree.root, depth = 0) {
11030
- const node = this._tree.nodes[root];
11031
- const result = callback(node, depth);
11032
- if (result !== void 0) {
11033
- return result;
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);
11034
10982
  }
11035
- for (const childId of node.children) {
11036
- const result2 = this.tranverse(callback, childId, depth + 1);
11037
- if (result2 !== void 0) {
11038
- return result2;
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
+ };
11039
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;
11040
11042
  }
11041
- }
11042
- getNode(id) {
11043
- const node = this._tree.nodes[id];
11044
- invariant(node, void 0, {
11045
- F: __dxlog_file,
11046
- L: 98,
11047
- S: this,
11048
- A: [
11049
- "node",
11050
- ""
11051
- ]
11043
+ const rect = el.getBoundingClientRect();
11044
+ setSize({
11045
+ width: rect.width,
11046
+ height: rect.height
11052
11047
  });
11053
- return node;
11054
- }
11055
- /**
11056
- * Get the children of a node.
11057
- */
11058
- getChildNodes(node) {
11059
- return node.children.map((id) => this.getNode(id));
11060
- }
11061
- /**
11062
- * Get the parent of a node.
11063
- */
11064
- getParent(node) {
11065
- const parent = this.tranverse((n) => {
11066
- if (n.children.includes(node.id)) {
11067
- return n;
11048
+ const observer = new ResizeObserver((entries) => {
11049
+ const entry = entries[0];
11050
+ if (!entry) {
11051
+ return;
11068
11052
  }
11053
+ const { width, height } = entry.contentRect;
11054
+ setSize((prev) => prev.width === width && prev.height === height ? prev : {
11055
+ width,
11056
+ height
11057
+ });
11069
11058
  });
11070
- return parent ?? null;
11071
- }
11072
- /**
11073
- * Get the next node in the tree.
11074
- */
11075
- getNext(node, hierarchical = true) {
11076
- if (hierarchical && node.children.length) {
11077
- return this.getChildNodes(node)[0];
11078
- } else {
11079
- const parent = this.getParent(node);
11080
- if (parent) {
11081
- const idx = this.getChildNodes(parent).findIndex(({ id }) => id === node.id);
11082
- if (idx < parent.children.length - 1) {
11083
- return this.getNode(parent.children[idx + 1]);
11084
- } else {
11085
- return this.getNext(parent, false);
11086
- }
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;
11087
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;
11088
11099
  }
11100
+ renderLattice(svgRef.current, cells, {
11101
+ width,
11102
+ height,
11103
+ padding,
11104
+ onNodeHover: (n, e) => handleHoverRef.current?.(n, e)
11105
+ });
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;
11089
11133
  }
11090
- /**
11091
- * Get the previous node in the tree.
11092
- */
11093
- getPrevious(node, hierarchical = true) {
11094
- const parent = this.getParent(node);
11095
- const idx = this.getChildNodes(parent).findIndex(({ id }) => id === node.id);
11096
- if (idx === 0) {
11097
- if (hierarchical) {
11098
- return parent;
11099
- }
11100
- } else {
11101
- const previous = this.getNode(parent.children[idx - 1]);
11102
- if (hierarchical && previous.children.length) {
11103
- return this.getLastDescendent(previous);
11104
- }
11105
- return previous;
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));
11168
+ };
11169
+
11170
+ // src/components/Tree/Tree.tsx
11171
+ import React9, { useMemo as useMemo6 } from "react";
11172
+
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
11197
+ ]);
11198
+ const handleHoverRef = useRef4(() => {
11199
+ });
11200
+ handleHoverRef.current = (node, event) => onNodeHover?.(node, event);
11201
+ useEffect7(() => {
11202
+ if (!svgRef.current || !width || !height) {
11203
+ return;
11106
11204
  }
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 = [];
11107
11242
  }
11108
- /**
11109
- * Get the last descendent of a node.
11110
- */
11111
- getLastDescendent(node) {
11112
- const children = this.getChildNodes(node);
11113
- const last = children.length ? children[children.length - 1] : void 0;
11114
- if (last) {
11115
- return this.getLastDescendent(last);
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;
11116
11248
  }
11117
- return node;
11249
+ source.outgoing.push([
11250
+ source,
11251
+ target,
11252
+ edge
11253
+ ]);
11254
+ target.incoming.push([
11255
+ source,
11256
+ target,
11257
+ edge
11258
+ ]);
11118
11259
  }
11119
- //
11120
- // Mutations
11121
- //
11122
- /**
11123
- * Clear tree.
11124
- */
11125
- clear() {
11126
- const root = this._tree.nodes[this._tree.root];
11127
- root.children.length = 0;
11128
- this._tree.nodes = {
11129
- [root.id]: root
11130
- };
11260
+ return root;
11261
+ };
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;
11131
11268
  }
11132
- /**
11133
- * Add node.
11134
- */
11135
- addNode(parent, node, index) {
11136
- if (!node) {
11137
- const id = Key.ObjectId.random();
11138
- node = {
11139
- id,
11140
- children: [],
11141
- data: {
11142
- text: ""
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);
11303
+ };
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;
11143
11341
  }
11144
- };
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);
11145
11356
  }
11146
- this._tree.nodes[node.id] = node;
11147
- parent.children.splice(index ?? parent.children.length, 0, node.id);
11148
- return node;
11149
11357
  }
11150
- /**
11151
- * Delete node.
11152
- */
11153
- deleteNode(parent, id) {
11154
- const node = this._tree.nodes[id];
11155
- if (!node) {
11358
+ };
11359
+
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) {
11156
11370
  return void 0;
11157
11371
  }
11158
- delete this._tree.nodes[node.id];
11159
- const idx = parent.children.findIndex((child) => child === id);
11160
- if (idx !== -1) {
11161
- parent.children.splice(idx, 1);
11162
- }
11163
- return node;
11164
- }
11165
- /**
11166
- * Move child node.
11167
- */
11168
- moveNode(node, from, to) {
11169
- invariant(from >= 0 && from < node.children.length, void 0, {
11170
- F: __dxlog_file,
11171
- L: 228,
11172
- S: this,
11173
- A: [
11174
- "from >= 0 && from < node.children.length",
11175
- ""
11176
- ]
11177
- });
11178
- invariant(to >= 0 && to < node.children.length, void 0, {
11179
- F: __dxlog_file,
11180
- L: 229,
11181
- S: this,
11182
- A: [
11183
- "to >= 0 && to < node.children.length",
11184
- ""
11185
- ]
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;
11186
11393
  });
11187
- if (from === to) {
11188
- return null;
11189
- }
11190
- const child = node.children[from];
11191
- node.children.splice(from, 1);
11192
- node.children.splice(to, 0, child);
11193
- return this.getNode(child);
11194
- }
11195
- /**
11196
- * Indent node.
11197
- */
11198
- indentNode(node) {
11199
- const parent = this.getParent(node);
11200
- if (!parent) {
11201
- return;
11394
+ }, []);
11395
+ const handleClickRef = useRef5(() => {
11396
+ });
11397
+ handleClickRef.current = (node) => {
11398
+ onNodeClick?.(node);
11399
+ if (node.children?.length) {
11400
+ toggle(node.id);
11202
11401
  }
11203
- const idx = parent.children.findIndex((child) => child === node.id);
11204
- if (idx < 1 || idx >= parent.children.length) {
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) {
11205
11412
  return;
11206
11413
  }
11207
- const previous = this.getNode(parent.children[idx - 1]);
11208
- parent.children.splice(idx, 1);
11209
- previous.children.push(node.id);
11210
- }
11211
- /**
11212
- * Unindent node.
11213
- */
11214
- unindentNode(node) {
11215
- const parent = this.getParent(node);
11216
- if (!parent) {
11217
- return;
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,
11453
+ radius
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();
11482
+ };
11483
+
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);
11218
11510
  }
11219
- const ancestor = this.getParent(parent);
11220
- if (!ancestor) {
11511
+ };
11512
+ const root = useMemo5(() => buildHierarchy(data, collapsed), [
11513
+ data,
11514
+ collapsed
11515
+ ]);
11516
+ useEffect9(() => {
11517
+ if (!svgRef.current || !width || !height) {
11221
11518
  return;
11222
11519
  }
11223
- const nodeIdx = parent.children.findIndex((id) => id === node.id);
11224
- const [_, ...rest] = parent.children.splice(nodeIdx, parent.children.length - nodeIdx);
11225
- parent.children.splice(nodeIdx, parent.children.length - nodeIdx);
11226
- const parentIdx = this.getChildNodes(ancestor).findIndex((n) => n.id === parent.id);
11227
- ancestor.children.splice(parentIdx + 1, 0, node.id);
11228
- node.children.push(...rest);
11229
- }
11230
- constructor(tree3) {
11231
- _define_property(this, "_tree", void 0);
11232
- this._tree = tree3 ?? _Tree.create();
11233
- }
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
+ }));
11234
11550
  };
11235
- _define_property(Tree, "create", () => {
11236
- const id = Key.ObjectId.random();
11237
- return Obj.make(TreeType, {
11238
- root: id,
11239
- nodes: {
11240
- [id]: {
11241
- id,
11242
- children: [],
11243
- data: {
11244
- text: ""
11245
- }
11246
- }
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([
11557
+ dx,
11558
+ dy
11559
+ ])(root);
11560
+ let x0 = Infinity;
11561
+ let x1 = -x0;
11562
+ root.each((d) => {
11563
+ if (d.x > x1) {
11564
+ x1 = d.x;
11565
+ }
11566
+ if (d.x < x0) {
11567
+ x0 = d.x;
11247
11568
  }
11248
11569
  });
11249
- });
11250
-
11251
- // src/components/Tree/types/types.ts
11252
- var mapGraphToTreeData = (model, maxDepth = 8) => {
11253
- let data;
11254
- 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();
11255
11604
  };
11256
11605
 
11257
11606
  // src/components/Tree/Tree.tsx
11258
- var defaultTreeLayoutSlots = {
11259
- node: "fill-blue-600",
11260
- path: "fill-none stroke-blue-400 stroke-[0.5px]",
11261
- text: "stroke-[0.5px] stroke-neutral-700 text-xs"
11262
- };
11263
- var renderers = /* @__PURE__ */ new Map([
11264
- [
11265
- "tidy",
11266
- TidyTree_default
11267
- ],
11268
- [
11269
- "radial",
11270
- RadialTree_default
11271
- ],
11272
- [
11273
- "edge",
11274
- HierarchicalEdgeBundling_default
11275
- ]
11276
- ]);
11277
- var Tree2 = ({ space, selected, variant = "tidy", onNodeClick }) => {
11278
- var _effect = _useSignals3();
11279
- try {
11280
- const [model] = useAsyncState(async () => space ? new SpaceGraphModel().open(space) : void 0, [
11281
- space,
11282
- selected
11283
- ]);
11284
- const [tree3, setTree] = useState();
11285
- useEffect3(() => {
11286
- return model?.subscribe(() => {
11287
- const tree4 = mapGraphToTreeData(model);
11288
- setTree(tree4);
11289
- }, true);
11290
- }, [
11291
- model
11292
- ]);
11293
- const context = useRef(null);
11294
- useEffect3(() => {
11295
- if (context.current?.size) {
11296
- const { width, height } = context.current.size;
11297
- const size = Math.min(width, height);
11298
- const radius = size * 0.4;
11299
- const options = {
11300
- // TODO(burdon): Type.
11301
- label: (d) => d.label ?? d.id,
11302
- width,
11303
- height,
11304
- radius,
11305
- marginLeft: (width - radius * 2) / 2,
11306
- marginRight: (width - radius * 2) / 2,
11307
- marginTop: (height - radius * 2) / 2,
11308
- marginBottom: (height - radius * 2) / 2,
11309
- slots: defaultTreeLayoutSlots
11310
- };
11311
- if (tree3) {
11312
- const renderer = renderers.get(variant);
11313
- renderer?.(context.current.svg, tree3, options);
11314
- }
11315
- }
11316
- }, [
11317
- context.current,
11318
- tree3
11319
- ]);
11320
- return /* @__PURE__ */ React3.createElement("div", {
11321
- onClick: () => onNodeClick?.()
11322
- }, /* @__PURE__ */ React3.createElement(SVG.Root, {
11323
- ref: context
11324
- }));
11325
- } finally {
11326
- _effect.f();
11327
- }
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
+ ]);
11328
11650
  };
11329
-
11330
- // src/components/index.ts
11331
- import { lazy } from "react";
11332
- var ExplorerContainer = lazy(() => import("./ExplorerContainer-6SHZJ6AK.mjs"));
11333
-
11334
11651
  export {
11652
+ CanvasForceGraph,
11335
11653
  Chart,
11654
+ ForceGraph,
11336
11655
  Globe,
11656
+ HierarchicalEdgeBundling,
11657
+ Lattice,
11658
+ RadialTree,
11659
+ TidyTree,
11660
+ Tree,
11661
+ buildHierarchy,
11337
11662
  defaultTreeLayoutSlots,
11338
- Tree2 as Tree,
11339
- ExplorerContainer
11663
+ isCollapsed,
11664
+ isLeaf,
11665
+ treeTypeToTreeNode
11340
11666
  };
11341
- //# sourceMappingURL=chunk-MOM5KCKC.mjs.map
11667
+ //# sourceMappingURL=index.mjs.map