@dxos/plugin-explorer 0.8.4-main.7996785055 → 0.8.4-main.8baae0fced

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