@dxos/plugin-explorer 0.8.4-main.ae835ea → 0.8.4-main.bc2380dfbc

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 (234) hide show
  1. package/LICENSE +102 -5
  2. package/dist/lib/neutral/ExplorerArticle-EW2MBCRK.mjs +141 -0
  3. package/dist/lib/neutral/ExplorerArticle-EW2MBCRK.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/neutral/chunk-7SPMPHRS.mjs +72 -0
  8. package/dist/lib/neutral/chunk-7SPMPHRS.mjs.map +7 -0
  9. package/dist/lib/neutral/chunk-GRJXLL4Z.mjs +25 -0
  10. package/dist/lib/neutral/chunk-GRJXLL4Z.mjs.map +7 -0
  11. package/dist/lib/{browser/chunk-UBHZGWZQ.mjs → neutral/chunk-HPIS2WXY.mjs} +2 -2
  12. package/dist/lib/neutral/chunk-HPIS2WXY.mjs.map +7 -0
  13. package/dist/lib/{browser/chunk-CRN65FY3.mjs → neutral/components/index.mjs} +817 -288
  14. package/dist/lib/{node-esm/chunk-RSZFBKZM.mjs.map → neutral/components/index.mjs.map} +4 -4
  15. package/dist/lib/neutral/containers/index.mjs +9 -0
  16. package/dist/lib/neutral/containers/index.mjs.map +7 -0
  17. package/dist/lib/neutral/create-object-F6TKVAGV.mjs +39 -0
  18. package/dist/lib/neutral/create-object-F6TKVAGV.mjs.map +7 -0
  19. package/dist/lib/neutral/hooks/index.mjs +45 -0
  20. package/dist/lib/neutral/hooks/index.mjs.map +7 -0
  21. package/dist/lib/neutral/index.mjs +14 -0
  22. package/dist/lib/neutral/meta.json +1 -0
  23. package/dist/lib/{browser → neutral}/meta.mjs +1 -1
  24. package/dist/lib/neutral/plugin.mjs +12 -0
  25. package/dist/lib/neutral/plugin.mjs.map +7 -0
  26. package/dist/lib/neutral/react-surface-APBW2VQG.mjs +26 -0
  27. package/dist/lib/neutral/react-surface-APBW2VQG.mjs.map +7 -0
  28. package/dist/lib/neutral/testing.mjs +8 -0
  29. package/dist/lib/neutral/translations.mjs +33 -0
  30. package/dist/lib/neutral/translations.mjs.map +7 -0
  31. package/dist/lib/{browser → neutral}/types/index.mjs +1 -2
  32. package/dist/types/data/cities.d.ts +4 -4
  33. package/dist/types/data/cities.d.ts.map +1 -1
  34. package/dist/types/data/countries-110m.d.ts +19 -22
  35. package/dist/types/data/countries-110m.d.ts.map +1 -1
  36. package/dist/types/src/ExplorerPlugin.d.ts +3 -1
  37. package/dist/types/src/ExplorerPlugin.d.ts.map +1 -1
  38. package/dist/types/src/ExplorerPlugin.test.d.ts +2 -0
  39. package/dist/types/src/ExplorerPlugin.test.d.ts.map +1 -0
  40. package/dist/types/src/capabilities/create-object.d.ts +11 -0
  41. package/dist/types/src/capabilities/create-object.d.ts.map +1 -0
  42. package/dist/types/src/capabilities/index.d.ts +8 -2
  43. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  44. package/dist/types/src/capabilities/react-surface.d.ts +3 -2
  45. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  46. package/dist/types/src/components/Chart/Chart.d.ts.map +1 -1
  47. package/dist/types/src/components/Chart/Chart.stories.d.ts +4 -1
  48. package/dist/types/src/components/Chart/Chart.stories.d.ts.map +1 -1
  49. package/dist/types/src/components/Globe/Globe.d.ts.map +1 -1
  50. package/dist/types/src/components/Globe/Globe.stories.d.ts +5 -2
  51. package/dist/types/src/components/Globe/Globe.stories.d.ts.map +1 -1
  52. package/dist/types/src/components/Graph/CanvasForceGraph.d.ts +13 -0
  53. package/dist/types/src/components/Graph/CanvasForceGraph.d.ts.map +1 -0
  54. package/dist/types/src/components/Graph/CanvasForceGraph.stories.d.ts +17 -0
  55. package/dist/types/src/components/Graph/CanvasForceGraph.stories.d.ts.map +1 -0
  56. package/dist/types/src/components/Graph/ForceGraph.d.ts +12 -5
  57. package/dist/types/src/components/Graph/ForceGraph.d.ts.map +1 -1
  58. package/dist/types/src/components/Graph/ForceGraph.stories.d.ts +4 -2
  59. package/dist/types/src/components/Graph/ForceGraph.stories.d.ts.map +1 -1
  60. package/dist/types/src/components/Graph/{adapter.d.ts → graph-adapter.d.ts} +2 -2
  61. package/dist/types/src/components/Graph/graph-adapter.d.ts.map +1 -0
  62. package/dist/types/src/components/Graph/index.d.ts +1 -1
  63. package/dist/types/src/components/Graph/index.d.ts.map +1 -1
  64. package/dist/types/src/components/Tree/EdgeBundling.stories.d.ts +21 -0
  65. package/dist/types/src/components/Tree/EdgeBundling.stories.d.ts.map +1 -0
  66. package/dist/types/src/components/Tree/Tree.d.ts +20 -23
  67. package/dist/types/src/components/Tree/Tree.d.ts.map +1 -1
  68. package/dist/types/src/components/Tree/Tree.stories.d.ts +5 -12
  69. package/dist/types/src/components/Tree/Tree.stories.d.ts.map +1 -1
  70. package/dist/types/src/components/Tree/index.d.ts +3 -0
  71. package/dist/types/src/components/Tree/index.d.ts.map +1 -1
  72. package/dist/types/src/components/Tree/layout/HierarchicalEdgeBundling.d.ts +35 -2
  73. package/dist/types/src/components/Tree/layout/HierarchicalEdgeBundling.d.ts.map +1 -1
  74. package/dist/types/src/components/Tree/layout/RadialTree.d.ts +35 -2
  75. package/dist/types/src/components/Tree/layout/RadialTree.d.ts.map +1 -1
  76. package/dist/types/src/components/Tree/layout/TidyTree.d.ts +24 -2
  77. package/dist/types/src/components/Tree/layout/TidyTree.d.ts.map +1 -1
  78. package/dist/types/src/components/Tree/layout/hierarchy.d.ts +17 -0
  79. package/dist/types/src/components/Tree/layout/hierarchy.d.ts.map +1 -0
  80. package/dist/types/src/components/Tree/layout/index.d.ts +5 -4
  81. package/dist/types/src/components/Tree/layout/index.d.ts.map +1 -1
  82. package/dist/types/src/components/Tree/layout/slots.d.ts +7 -0
  83. package/dist/types/src/components/Tree/layout/slots.d.ts.map +1 -0
  84. package/dist/types/src/components/Tree/layout/useContainerSize.d.ts +15 -0
  85. package/dist/types/src/components/Tree/layout/useContainerSize.d.ts.map +1 -0
  86. package/dist/types/src/components/Tree/space-graph-adapter.d.ts +32 -0
  87. package/dist/types/src/components/Tree/space-graph-adapter.d.ts.map +1 -0
  88. package/dist/types/src/components/Tree/testing/generator.d.ts.map +1 -1
  89. package/dist/types/src/components/Tree/testing/index.d.ts +1 -0
  90. package/dist/types/src/components/Tree/testing/index.d.ts.map +1 -1
  91. package/dist/types/src/components/Tree/testing/relations.d.ts +47 -0
  92. package/dist/types/src/components/Tree/testing/relations.d.ts.map +1 -0
  93. package/dist/types/src/components/Tree/types/tree.d.ts +18 -16
  94. package/dist/types/src/components/Tree/types/tree.d.ts.map +1 -1
  95. package/dist/types/src/components/Tree/types/types.d.ts +14 -4
  96. package/dist/types/src/components/Tree/types/types.d.ts.map +1 -1
  97. package/dist/types/src/components/index.d.ts +0 -4
  98. package/dist/types/src/components/index.d.ts.map +1 -1
  99. package/dist/types/src/components/plot.d.ts.map +1 -1
  100. package/dist/types/src/containers/ExplorerArticle/ExplorerArticle.d.ts +8 -0
  101. package/dist/types/src/containers/ExplorerArticle/ExplorerArticle.d.ts.map +1 -0
  102. package/dist/types/src/containers/ExplorerArticle/ExplorerArticle.stories.d.ts +24 -0
  103. package/dist/types/src/containers/ExplorerArticle/ExplorerArticle.stories.d.ts.map +1 -0
  104. package/dist/types/src/containers/ExplorerArticle/index.d.ts +2 -0
  105. package/dist/types/src/containers/ExplorerArticle/index.d.ts.map +1 -0
  106. package/dist/types/src/containers/index.d.ts +3 -0
  107. package/dist/types/src/containers/index.d.ts.map +1 -0
  108. package/dist/types/src/hooks/useGraphModel.d.ts +2 -2
  109. package/dist/types/src/hooks/useGraphModel.d.ts.map +1 -1
  110. package/dist/types/src/index.d.ts +1 -3
  111. package/dist/types/src/index.d.ts.map +1 -1
  112. package/dist/types/src/meta.d.ts +2 -2
  113. package/dist/types/src/meta.d.ts.map +1 -1
  114. package/dist/types/src/plugin.d.ts +3 -0
  115. package/dist/types/src/plugin.d.ts.map +1 -0
  116. package/dist/types/src/testing.d.ts +2 -0
  117. package/dist/types/src/testing.d.ts.map +1 -0
  118. package/dist/types/src/translations.d.ts +31 -22
  119. package/dist/types/src/translations.d.ts.map +1 -1
  120. package/dist/types/src/types/ExplorerAction.d.ts +1 -18
  121. package/dist/types/src/types/ExplorerAction.d.ts.map +1 -1
  122. package/dist/types/src/types/Graph.d.ts +14 -25
  123. package/dist/types/src/types/Graph.d.ts.map +1 -1
  124. package/dist/types/tsconfig.tsbuildinfo +1 -1
  125. package/package.json +113 -61
  126. package/src/ExplorerPlugin.test.ts +26 -0
  127. package/src/ExplorerPlugin.tsx +15 -56
  128. package/src/capabilities/create-object.ts +36 -0
  129. package/src/capabilities/index.ts +3 -3
  130. package/src/capabilities/react-surface.tsx +24 -19
  131. package/src/components/Chart/Chart.stories.tsx +16 -23
  132. package/src/components/Globe/Globe.stories.tsx +19 -22
  133. package/src/components/Graph/CanvasForceGraph.stories.tsx +83 -0
  134. package/src/components/Graph/CanvasForceGraph.tsx +124 -0
  135. package/src/components/Graph/ForceGraph.stories.tsx +79 -41
  136. package/src/components/Graph/ForceGraph.tsx +104 -85
  137. package/src/components/Graph/{adapter.ts → graph-adapter.ts} +14 -8
  138. package/src/components/Graph/index.ts +1 -1
  139. package/src/components/Tree/EdgeBundling.stories.tsx +144 -0
  140. package/src/components/Tree/Tree.stories.tsx +20 -38
  141. package/src/components/Tree/Tree.tsx +69 -95
  142. package/src/components/Tree/index.ts +3 -0
  143. package/src/components/Tree/layout/HierarchicalEdgeBundling.tsx +277 -0
  144. package/src/components/Tree/layout/RadialTree.tsx +237 -0
  145. package/src/components/Tree/layout/TidyTree.tsx +246 -0
  146. package/src/components/Tree/layout/hierarchy.ts +32 -0
  147. package/src/components/Tree/layout/index.ts +5 -5
  148. package/src/components/Tree/layout/slots.ts +19 -0
  149. package/src/components/Tree/layout/useContainerSize.ts +43 -0
  150. package/src/components/Tree/space-graph-adapter.ts +96 -0
  151. package/src/components/Tree/testing/generator.ts +4 -2
  152. package/src/components/Tree/testing/index.ts +1 -0
  153. package/src/components/Tree/testing/relations.ts +182 -0
  154. package/src/components/Tree/types/tree.test.ts +7 -6
  155. package/src/components/Tree/types/tree.ts +41 -20
  156. package/src/components/Tree/types/types.ts +38 -29
  157. package/src/components/index.ts +0 -4
  158. package/src/containers/ExplorerArticle/ExplorerArticle.stories.tsx +119 -0
  159. package/src/containers/ExplorerArticle/ExplorerArticle.tsx +153 -0
  160. package/src/containers/ExplorerArticle/index.ts +5 -0
  161. package/src/containers/index.ts +7 -0
  162. package/src/hooks/useGraphModel.ts +25 -14
  163. package/src/index.ts +1 -4
  164. package/src/meta.ts +3 -3
  165. package/src/plugin.ts +9 -0
  166. package/src/testing.ts +7 -0
  167. package/src/translations.ts +16 -13
  168. package/src/types/ExplorerAction.ts +10 -19
  169. package/src/types/Graph.ts +25 -22
  170. package/src/typings.d.ts +8 -0
  171. package/dist/lib/browser/ExplorerContainer-L5RVUJRL.mjs +0 -50
  172. package/dist/lib/browser/ExplorerContainer-L5RVUJRL.mjs.map +0 -7
  173. package/dist/lib/browser/chunk-2MKBRIUT.mjs +0 -31
  174. package/dist/lib/browser/chunk-2MKBRIUT.mjs.map +0 -7
  175. package/dist/lib/browser/chunk-6BVXZQPP.mjs +0 -188
  176. package/dist/lib/browser/chunk-6BVXZQPP.mjs.map +0 -7
  177. package/dist/lib/browser/chunk-BGNRYZUN.mjs +0 -79
  178. package/dist/lib/browser/chunk-BGNRYZUN.mjs.map +0 -7
  179. package/dist/lib/browser/chunk-CRN65FY3.mjs.map +0 -7
  180. package/dist/lib/browser/chunk-UBHZGWZQ.mjs.map +0 -7
  181. package/dist/lib/browser/index.mjs +0 -119
  182. package/dist/lib/browser/index.mjs.map +0 -7
  183. package/dist/lib/browser/intent-resolver-FX5H52QN.mjs +0 -31
  184. package/dist/lib/browser/intent-resolver-FX5H52QN.mjs.map +0 -7
  185. package/dist/lib/browser/meta.json +0 -1
  186. package/dist/lib/browser/react-surface-VS3ZFL2Y.mjs +0 -35
  187. package/dist/lib/browser/react-surface-VS3ZFL2Y.mjs.map +0 -7
  188. package/dist/lib/node-esm/ExplorerContainer-BBLPHH7K.mjs +0 -51
  189. package/dist/lib/node-esm/ExplorerContainer-BBLPHH7K.mjs.map +0 -7
  190. package/dist/lib/node-esm/chunk-3ODK27PU.mjs +0 -33
  191. package/dist/lib/node-esm/chunk-3ODK27PU.mjs.map +0 -7
  192. package/dist/lib/node-esm/chunk-CRSVAZNA.mjs +0 -190
  193. package/dist/lib/node-esm/chunk-CRSVAZNA.mjs.map +0 -7
  194. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +0 -11
  195. package/dist/lib/node-esm/chunk-RSZFBKZM.mjs +0 -11091
  196. package/dist/lib/node-esm/chunk-TQESRBUJ.mjs +0 -80
  197. package/dist/lib/node-esm/chunk-TQESRBUJ.mjs.map +0 -7
  198. package/dist/lib/node-esm/chunk-UXZM5VJB.mjs +0 -26
  199. package/dist/lib/node-esm/chunk-UXZM5VJB.mjs.map +0 -7
  200. package/dist/lib/node-esm/index.mjs +0 -120
  201. package/dist/lib/node-esm/index.mjs.map +0 -7
  202. package/dist/lib/node-esm/intent-resolver-RGBBXXYM.mjs +0 -32
  203. package/dist/lib/node-esm/intent-resolver-RGBBXXYM.mjs.map +0 -7
  204. package/dist/lib/node-esm/meta.json +0 -1
  205. package/dist/lib/node-esm/meta.mjs +0 -9
  206. package/dist/lib/node-esm/react-surface-SXPT2T37.mjs +0 -36
  207. package/dist/lib/node-esm/react-surface-SXPT2T37.mjs.map +0 -7
  208. package/dist/lib/node-esm/types/index.mjs +0 -12
  209. package/dist/types/src/capabilities/intent-resolver.d.ts +0 -4
  210. package/dist/types/src/capabilities/intent-resolver.d.ts.map +0 -1
  211. package/dist/types/src/components/ExplorerContainer.d.ts +0 -9
  212. package/dist/types/src/components/ExplorerContainer.d.ts.map +0 -1
  213. package/dist/types/src/components/Graph/D3ForceGraph.d.ts +0 -14
  214. package/dist/types/src/components/Graph/D3ForceGraph.d.ts.map +0 -1
  215. package/dist/types/src/components/Graph/D3ForceGraph.stories.d.ts +0 -15
  216. package/dist/types/src/components/Graph/D3ForceGraph.stories.d.ts.map +0 -1
  217. package/dist/types/src/components/Graph/adapter.d.ts.map +0 -1
  218. package/dist/types/src/components/Graph/testing.d.ts +0 -14
  219. package/dist/types/src/components/Graph/testing.d.ts.map +0 -1
  220. package/src/capabilities/intent-resolver.ts +0 -21
  221. package/src/components/ExplorerContainer.tsx +0 -54
  222. package/src/components/Graph/D3ForceGraph.stories.tsx +0 -77
  223. package/src/components/Graph/D3ForceGraph.tsx +0 -101
  224. package/src/components/Graph/testing.ts +0 -55
  225. package/src/components/Tree/layout/HierarchicalEdgeBundling.ts +0 -162
  226. package/src/components/Tree/layout/RadialTree.ts +0 -94
  227. package/src/components/Tree/layout/TidyTree.ts +0 -101
  228. /package/dist/lib/{browser/chunk-J5LGTIGS.mjs.map → neutral/ExplorerPlugin.mjs.map} +0 -0
  229. /package/dist/lib/{browser → neutral}/chunk-J5LGTIGS.mjs +0 -0
  230. /package/dist/lib/{browser/meta.mjs.map → neutral/chunk-J5LGTIGS.mjs.map} +0 -0
  231. /package/dist/lib/{browser/types → neutral}/index.mjs.map +0 -0
  232. /package/dist/lib/{node-esm → neutral}/meta.mjs.map +0 -0
  233. /package/dist/lib/{node-esm/chunk-HSLMI22Q.mjs.map → neutral/testing.mjs.map} +0 -0
  234. /package/dist/lib/{node-esm → neutral}/types/index.mjs.map +0 -0
@@ -0,0 +1,153 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import { useAtomValue } from '@effect-atom/atom-react';
6
+ import React, { useCallback, useEffect, useMemo, useState } from 'react';
7
+
8
+ import { type AppSurface } from '@dxos/app-toolkit/ui';
9
+ import { type Filter, Obj, type View } from '@dxos/echo';
10
+ import { QueryBuilder } from '@dxos/echo-query';
11
+ import { useObject } from '@dxos/react-client/echo';
12
+ import { DxAnchorActivate, Icon, Panel, Toolbar } from '@dxos/react-ui';
13
+ import { QueryEditor, type QueryEditorProps } from '@dxos/react-ui-components';
14
+
15
+ import { ForceGraph } from '#components';
16
+ import { HierarchicalEdgeBundling, RadialTree, spaceGraphToHierarchy, type TreeNode } from '#components';
17
+ import { useGraphModel } from '#hooks';
18
+
19
+ /** Visualization variants exposed by `ExplorerArticle`. */
20
+ export type ExplorerArticleVariant = 'force' | 'cluster' | 'bundle';
21
+
22
+ const VARIANTS: { value: ExplorerArticleVariant; icon: string; label: string }[] = [
23
+ {
24
+ value: 'force',
25
+ icon: 'ph--graph--regular',
26
+ label: 'Force-directed',
27
+ },
28
+ {
29
+ value: 'cluster',
30
+ icon: 'ph--asterisk-simple--regular',
31
+ label: 'Radial cluster',
32
+ },
33
+ {
34
+ value: 'bundle',
35
+ icon: 'ph--circles-three-plus--regular',
36
+ label: 'Edge bundling',
37
+ },
38
+ ];
39
+
40
+ export type ExplorerArticleProps = AppSurface.ObjectArticleProps<View.View>;
41
+
42
+ export const ExplorerArticle = ({ role, subject, variant }: ExplorerArticleProps) => {
43
+ const [view] = useObject(subject);
44
+ const db = view && Obj.getDatabase(view);
45
+ const [filter, setFilter] = useState<Filter.Any>();
46
+ const model = useGraphModel(db, filter);
47
+
48
+ const builder = useMemo(() => new QueryBuilder(), []);
49
+ const handleChange = useCallback<NonNullable<QueryEditorProps['onChange']>>((value) => {
50
+ setFilter(builder.build(value).filter);
51
+ }, []);
52
+
53
+ // The `variant` prop is the initial value; user can toggle via the toolbar tabs.
54
+ const [selected, setSelected] = useState<ExplorerArticleVariant>(isVariant(variant) ? variant : 'force');
55
+ useEffect(() => {
56
+ if (isVariant(variant)) {
57
+ setSelected(variant);
58
+ }
59
+ }, [variant]);
60
+ const handleVariantChange = useCallback((value: string) => {
61
+ if (isVariant(value)) {
62
+ setSelected(value);
63
+ }
64
+ }, []);
65
+
66
+ const handleHoverPreview = useCallback((node: TreeNode | null, event?: MouseEvent) => {
67
+ if (!node || !event) {
68
+ return;
69
+ }
70
+ const obj = node.data;
71
+ if (!obj || !Obj.isObject(obj)) {
72
+ return;
73
+ }
74
+ const dxn = Obj.getDXN(obj)?.toString();
75
+ if (!dxn) {
76
+ return;
77
+ }
78
+ const target = event.target as HTMLElement;
79
+ target.dispatchEvent(
80
+ new DxAnchorActivate({
81
+ dxn,
82
+ label: Obj.getLabel(obj) ?? dxn,
83
+ trigger: target,
84
+ kind: 'card',
85
+ }),
86
+ );
87
+ }, []);
88
+
89
+ const showToolbar = role === 'article';
90
+
91
+ if (!db || !model) {
92
+ return null;
93
+ }
94
+
95
+ return (
96
+ <Panel.Root role={role}>
97
+ {showToolbar && (
98
+ <Panel.Toolbar asChild>
99
+ <Toolbar.Root>
100
+ <QueryEditor db={db} onChange={handleChange} />
101
+ <Toolbar.ToggleGroup type='single' value={selected} onValueChange={handleVariantChange}>
102
+ {VARIANTS.map(({ value, icon, label }) => (
103
+ <Toolbar.ToggleGroupItem key={value} value={value} aria-label={label} title={label}>
104
+ <Icon icon={icon} size={4} />
105
+ </Toolbar.ToggleGroupItem>
106
+ ))}
107
+ </Toolbar.ToggleGroup>
108
+ </Toolbar.Root>
109
+ </Panel.Toolbar>
110
+ )}
111
+ <Panel.Content>
112
+ <Visualization variant={selected} model={model} onNodeHover={handleHoverPreview} />
113
+ </Panel.Content>
114
+ </Panel.Root>
115
+ );
116
+ };
117
+
118
+ const isVariant = (value: unknown): value is ExplorerArticleVariant =>
119
+ value === 'force' || value === 'cluster' || value === 'bundle';
120
+
121
+ type VisualizationProps = {
122
+ variant: ExplorerArticleVariant;
123
+ model: NonNullable<ReturnType<typeof useGraphModel>>;
124
+ onNodeHover?: (node: TreeNode | null, event?: MouseEvent) => void;
125
+ };
126
+
127
+ const Visualization = ({ variant, model, onNodeHover }: VisualizationProps) => {
128
+ if (variant === 'force') {
129
+ // ForceGraph subscribes to model.graphAtom internally; don't re-render the wrapper on every tick.
130
+ return (
131
+ <ForceGraph
132
+ model={model}
133
+ onInspect={(node, event) => onNodeHover?.({ id: node.id, data: node.data?.data?.object }, event)}
134
+ />
135
+ );
136
+ }
137
+
138
+ return <HierarchyVisualization variant={variant} model={model} onNodeHover={onNodeHover} />;
139
+ };
140
+
141
+ /**
142
+ * Read from the model's reactive graph atom so the hierarchy is rebuilt as objects/relations stream in.
143
+ */
144
+ const HierarchyVisualization = ({ variant, model, onNodeHover }: VisualizationProps) => {
145
+ // Capture the atom snapshot so the memo's dep list explicitly tracks each push from the atom.
146
+ const graphSnapshot = useAtomValue(model.graphAtom);
147
+ const { tree, edges } = useMemo(() => spaceGraphToHierarchy(model), [model, graphSnapshot]);
148
+ if (variant === 'cluster') {
149
+ return <RadialTree data={tree} cluster onNodeHover={onNodeHover} />;
150
+ }
151
+
152
+ return <HierarchicalEdgeBundling data={tree} edges={edges} onNodeHover={onNodeHover} />;
153
+ };
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ export { ExplorerArticle as default } from './ExplorerArticle';
@@ -0,0 +1,7 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import { type ComponentType, lazy } from 'react';
6
+
7
+ export const ExplorerArticle: ComponentType<any> = lazy(() => import('./ExplorerArticle'));
@@ -4,33 +4,44 @@
4
4
 
5
5
  import { useEffect, useState } from 'react';
6
6
 
7
- import { type Filter, type Queue, type Space } from '@dxos/client/echo';
7
+ import { Capabilities } from '@dxos/app-framework';
8
+ import { useCapability } from '@dxos/app-framework/ui';
9
+ import { type Database, type Entity, type Filter } from '@dxos/echo';
8
10
  import { SpaceGraphModel, type SpaceGraphModelOptions } from '@dxos/schema';
9
11
 
10
12
  // TODO(burdon): Factor out.
11
13
  export const useGraphModel = (
12
- space: Space | undefined,
14
+ db: Database.Database | undefined,
13
15
  filter?: Filter.Any | undefined,
14
16
  options?: SpaceGraphModelOptions,
15
- queue?: Queue,
17
+ items?: readonly Entity.Unknown[],
16
18
  ): SpaceGraphModel | undefined => {
19
+ const registry = useCapability(Capabilities.AtomRegistry);
17
20
  const [model, setModel] = useState<SpaceGraphModel | undefined>(undefined);
21
+
18
22
  useEffect(() => {
19
- if (!space) {
20
- void model?.close();
23
+ if (!db) {
21
24
  setModel(undefined);
22
25
  return;
23
26
  }
24
27
 
25
- // TODO(burdon): Does this need to be a dependency?
26
- if (!model || model.queue !== queue) {
27
- const model = new SpaceGraphModel().setFilter(filter).setOptions(options);
28
- void model.open(space, queue);
29
- setModel(model);
30
- } else {
31
- model.setFilter(filter).setOptions(options);
32
- }
33
- }, [space, filter, options, queue]);
28
+ const newModel = new SpaceGraphModel(registry);
29
+ void newModel.open(db);
30
+ setModel(newModel);
31
+
32
+ return () => {
33
+ setModel(undefined);
34
+ void newModel.close();
35
+ };
36
+ }, [db, registry]);
37
+
38
+ useEffect(() => {
39
+ model?.setFilter(filter).setOptions(options);
40
+ }, [model, filter, options]);
41
+
42
+ useEffect(() => {
43
+ model?.setItems(items);
44
+ }, [model, items]);
34
45
 
35
46
  return model;
36
47
  };
package/src/index.ts CHANGED
@@ -2,8 +2,5 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- export * from './components';
6
- export * from './hooks';
7
5
  export * from './meta';
8
-
9
- export * from './ExplorerPlugin';
6
+ export * from './types';
package/src/meta.ts CHANGED
@@ -2,11 +2,11 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import { type PluginMeta } from '@dxos/app-framework';
5
+ import { type Plugin } from '@dxos/app-framework';
6
6
  import { trim } from '@dxos/util';
7
7
 
8
- export const meta: PluginMeta = {
9
- id: 'dxos.org/plugin/explorer',
8
+ export const meta: Plugin.Meta = {
9
+ id: 'org.dxos.plugin.explorer',
10
10
  name: 'Explorer',
11
11
  description: trim`
12
12
  Interactive hypergraph visualization that reveals relationships between objects in your workspace.
package/src/plugin.ts ADDED
@@ -0,0 +1,9 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import { Plugin } from '@dxos/app-framework';
6
+
7
+ import { meta } from './meta';
8
+
9
+ export const ExplorerPlugin = Plugin.lazy(meta, () => import('#plugin'));
package/src/testing.ts ADDED
@@ -0,0 +1,7 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ // Eager re-export of `ExplorerPlugin`. See `@dxos/plugin-testing/src/core.ts`
6
+ // for the rationale.
7
+ export * from './ExplorerPlugin';
@@ -2,27 +2,30 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
+ import { Type } from '@dxos/echo';
5
6
  import { type Resource } from '@dxos/react-ui';
6
- import { translations as componentsTranslations } from '@dxos/react-ui-components';
7
+ import { translations as componentsTranslations } from '@dxos/react-ui-components/translations';
7
8
 
8
- import { meta } from './meta';
9
- import { Graph } from './types';
9
+ import { meta } from '#meta';
10
+ import { Graph } from '#types';
10
11
 
11
12
  export const translations = [
12
13
  {
13
14
  'en-US': {
14
- [Graph.Graph.typename]: {
15
- 'typename label': 'Explorer',
16
- 'typename label_zero': 'Explorers',
17
- 'typename label_one': 'Explorer',
18
- 'typename label_other': 'Explorers',
19
- 'object name placeholder': 'New explorer',
20
- 'rename object label': 'Rename explorer',
21
- 'delete object label': 'Delete explorer',
15
+ [Type.getTypename(Graph.Graph)]: {
16
+ 'typename.label': 'Explorer',
17
+ 'typename.label_zero': 'Explorers',
18
+ 'typename.label_one': 'Explorer',
19
+ 'typename.label_other': 'Explorers',
20
+ 'object-name.placeholder': 'New explorer',
21
+ 'add-object.label': 'Add explorer',
22
+ 'rename-object.label': 'Rename explorer',
23
+ 'delete-object.label': 'Delete explorer',
24
+ 'object-deleted.label': 'Explorer deleted',
22
25
  },
23
26
  [meta.id]: {
24
- 'plugin name': 'Explorer',
25
- 'object title label': 'Title',
27
+ 'plugin.name': 'Explorer',
28
+ 'object-title.label': 'Title',
26
29
  },
27
30
  },
28
31
  },
@@ -4,26 +4,17 @@
4
4
 
5
5
  import * as Schema from 'effect/Schema';
6
6
 
7
- import { SpaceSchema } from '@dxos/react-client/echo';
8
- import { DataType, TypenameAnnotationId } from '@dxos/schema';
9
-
10
- import { meta } from '../meta';
11
-
12
- const EXPLORER_ACTION = `${meta.id}/action`;
7
+ import { TypeInputOptionsAnnotation } from '@dxos/plugin-space';
13
8
 
14
9
  export const GraphProps = Schema.Struct({
15
10
  name: Schema.optional(Schema.String),
16
- typename: Schema.String.annotations({
17
- [TypenameAnnotationId]: ['used-static', 'dynamic'],
18
- title: 'Select graph record type',
19
- }),
11
+ // TODO(wittjosiah): This should be a query input instead.
12
+ typename: Schema.String.pipe(
13
+ Schema.annotations({ title: 'Select type' }),
14
+ TypeInputOptionsAnnotation.set({
15
+ location: ['database', 'runtime'],
16
+ kind: ['user'],
17
+ }),
18
+ Schema.optional,
19
+ ),
20
20
  });
21
-
22
- export class CreateGraph extends Schema.TaggedClass<CreateGraph>()(`${EXPLORER_ACTION}/create-graph`, {
23
- input: Schema.Struct({
24
- space: SpaceSchema,
25
- }).pipe(Schema.extend(GraphProps)),
26
- output: Schema.Struct({
27
- object: DataType.View,
28
- }),
29
- }) {}
@@ -4,42 +4,45 @@
4
4
 
5
5
  import * as Schema from 'effect/Schema';
6
6
 
7
- import { Filter, Obj, Query, QueryAST, Type } from '@dxos/echo';
8
- import { LabelAnnotation, ViewAnnotation } from '@dxos/echo/internal';
9
- import { type CreateViewFromSpaceProps, createViewFromSpace } from '@dxos/schema';
7
+ import { Annotation, Filter, Obj, Query, QueryAST, Ref, Type, View } from '@dxos/echo';
8
+ import { FormInputAnnotation, LabelAnnotation } from '@dxos/echo/internal';
9
+ import { ViewAnnotation } from '@dxos/schema';
10
10
 
11
- export const Graph = Schema.Struct({
11
+ const GraphSchema = Schema.Struct({
12
12
  name: Schema.optional(Schema.String),
13
+
14
+ view: Ref.Ref(View.View).pipe(FormInputAnnotation.set(false)),
15
+
13
16
  query: Schema.Struct({
14
17
  raw: Schema.optional(Schema.String),
15
18
  ast: QueryAST.Query,
16
- }).pipe(Schema.mutable),
19
+ }).pipe(FormInputAnnotation.set(false)),
17
20
  }).pipe(
18
- Type.Obj({
19
- typename: 'dxos.org/type/Graph',
21
+ Type.object({
22
+ typename: 'org.dxos.type.graph',
20
23
  version: '0.1.0',
21
24
  }),
22
25
  LabelAnnotation.set(['name']),
23
- ViewAnnotation.set(true),
26
+ ViewAnnotation.set(['view']),
27
+ Annotation.IconAnnotation.set({
28
+ icon: 'ph--graph--regular',
29
+ hue: 'green',
30
+ }),
24
31
  );
32
+ export interface Graph extends Schema.Schema.Type<typeof GraphSchema> {}
33
+ export const Graph: Type.Obj<Graph> = GraphSchema as any;
25
34
 
26
- export type Graph = Schema.Schema.Type<typeof Graph>;
27
-
28
- /**
29
- * Make a graph object.
30
- */
31
- export const make = (
32
- props: Obj.MakeProps<typeof Graph> = { query: { raw: '', ast: Query.select(Filter.nothing()).ast } },
33
- ) => Obj.make(Graph, props);
34
-
35
- type MakeViewProps = Omit<CreateViewFromSpaceProps, 'presentation'> & {
36
- presentation?: Omit<Obj.MakeProps<typeof Graph>, 'name'>;
35
+ type MakeProps = Omit<Partial<Obj.MakeProps<typeof Graph>>, 'view'> & {
36
+ view: View.View;
37
37
  };
38
38
 
39
39
  /**
40
40
  * Make a graph as a view of a data set.
41
41
  */
42
- export const makeView = async ({ presentation, ...props }: MakeViewProps) => {
43
- const graph = make(presentation);
44
- return createViewFromSpace({ ...props, presentation: graph });
42
+ export const make = ({
43
+ name,
44
+ query = { raw: '', ast: Query.select(Filter.nothing()).ast },
45
+ view,
46
+ }: MakeProps): Graph => {
47
+ return Obj.make(Graph, { name, view: Ref.make(view), query });
45
48
  };
@@ -0,0 +1,8 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ declare module '*.css' {
6
+ const content: undefined;
7
+ export default content;
8
+ }
@@ -1,50 +0,0 @@
1
- import {
2
- useGraphModel
3
- } from "./chunk-2MKBRIUT.mjs";
4
- import {
5
- D3ForceGraph
6
- } from "./chunk-6BVXZQPP.mjs";
7
- import "./chunk-J5LGTIGS.mjs";
8
-
9
- // src/components/ExplorerContainer.tsx
10
- import { useSignals as _useSignals } from "@preact-signals/safe-react/tracking";
11
- import React, { useCallback, useMemo, useState } from "react";
12
- import { QueryBuilder } from "@dxos/echo-query";
13
- import { useGlobalSearch } from "@dxos/plugin-search";
14
- import { getSpace } from "@dxos/react-client/echo";
15
- import { Toolbar } from "@dxos/react-ui";
16
- import { QueryEditor } from "@dxos/react-ui-components";
17
- import { StackItem } from "@dxos/react-ui-stack";
18
- var ExplorerContainer = ({ role, view }) => {
19
- var _effect = _useSignals();
20
- try {
21
- const space = getSpace(view);
22
- const [filter, setFilter] = useState();
23
- const model = useGraphModel(space, filter);
24
- const { match } = useGlobalSearch();
25
- const builder = useMemo(() => new QueryBuilder(), []);
26
- const handleChange = useCallback((value) => {
27
- setFilter(builder.build(value).filter);
28
- }, []);
29
- const showToolbar = role === "article";
30
- if (!space || !model) {
31
- return null;
32
- }
33
- return /* @__PURE__ */ React.createElement(StackItem.Content, {
34
- toolbar: showToolbar
35
- }, showToolbar && /* @__PURE__ */ React.createElement(Toolbar.Root, null, /* @__PURE__ */ React.createElement(QueryEditor, {
36
- db: space.db,
37
- onChange: handleChange
38
- })), /* @__PURE__ */ React.createElement(D3ForceGraph, {
39
- model,
40
- match
41
- }));
42
- } finally {
43
- _effect.f();
44
- }
45
- };
46
- var ExplorerContainer_default = ExplorerContainer;
47
- export {
48
- ExplorerContainer_default as default
49
- };
50
- //# sourceMappingURL=ExplorerContainer-L5RVUJRL.mjs.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/components/ExplorerContainer.tsx"],
4
- "sourcesContent": ["//\n// Copyright 2023 DXOS.org\n//\n\nimport React, { useCallback, useMemo, useState } from 'react';\n\nimport { type Filter } from '@dxos/echo';\nimport { QueryBuilder } from '@dxos/echo-query';\nimport { useGlobalSearch } from '@dxos/plugin-search';\nimport { getSpace } from '@dxos/react-client/echo';\nimport { Toolbar } from '@dxos/react-ui';\nimport { QueryEditor, type QueryEditorProps } from '@dxos/react-ui-components';\nimport { StackItem } from '@dxos/react-ui-stack';\nimport { type DataType } from '@dxos/schema';\n\nimport { useGraphModel } from '../hooks';\n\nimport { D3ForceGraph } from './Graph';\n\ntype ExplorerContainerProps = {\n role: string;\n view: DataType.View;\n};\n\nconst ExplorerContainer = ({ role, view }: ExplorerContainerProps) => {\n const space = getSpace(view);\n const [filter, setFilter] = useState<Filter.Any>();\n const model = useGraphModel(space, filter);\n const { match } = useGlobalSearch();\n\n const builder = useMemo(() => new QueryBuilder(), []);\n const handleChange = useCallback<NonNullable<QueryEditorProps['onChange']>>((value) => {\n setFilter(builder.build(value).filter);\n }, []);\n\n const showToolbar = role === 'article';\n\n if (!space || !model) {\n return null;\n }\n\n return (\n <StackItem.Content toolbar={showToolbar}>\n {showToolbar && (\n <Toolbar.Root>\n <QueryEditor db={space.db} onChange={handleChange} />\n </Toolbar.Root>\n )}\n <D3ForceGraph model={model} match={match} />\n </StackItem.Content>\n );\n};\n\nexport default ExplorerContainer;\n"],
5
- "mappings": ";;;;;;;;;;AAIA,OAAOA,SAASC,aAAaC,SAASC,gBAAgB;AAGtD,SAASC,oBAAoB;AAC7B,SAASC,uBAAuB;AAChC,SAASC,gBAAgB;AACzB,SAASC,eAAe;AACxB,SAASC,mBAA0C;AACnD,SAASC,iBAAiB;AAY1B,IAAMC,oBAAoB,CAAC,EAAEC,MAAMC,KAAI,MAA0B;;;AAC/D,UAAMC,QAAQC,SAASF,IAAAA;AACvB,UAAM,CAACG,QAAQC,SAAAA,IAAaC,SAAAA;AAC5B,UAAMC,QAAQC,cAAcN,OAAOE,MAAAA;AACnC,UAAM,EAAEK,MAAK,IAAKC,gBAAAA;AAElB,UAAMC,UAAUC,QAAQ,MAAM,IAAIC,aAAAA,GAAgB,CAAA,CAAE;AACpD,UAAMC,eAAeC,YAAuD,CAACC,UAAAA;AAC3EX,gBAAUM,QAAQM,MAAMD,KAAAA,EAAOZ,MAAM;IACvC,GAAG,CAAA,CAAE;AAEL,UAAMc,cAAclB,SAAS;AAE7B,QAAI,CAACE,SAAS,CAACK,OAAO;AACpB,aAAO;IACT;AAEA,WACE,sBAAA,cAACY,UAAUC,SAAO;MAACC,SAASH;OACzBA,eACC,sBAAA,cAACI,QAAQC,MAAI,MACX,sBAAA,cAACC,aAAAA;MAAYC,IAAIvB,MAAMuB;MAAIC,UAAUZ;SAGzC,sBAAA,cAACa,cAAAA;MAAapB;MAAcE;;;;;AAGlC;AAEA,IAAA,4BAAeV;",
6
- "names": ["React", "useCallback", "useMemo", "useState", "QueryBuilder", "useGlobalSearch", "getSpace", "Toolbar", "QueryEditor", "StackItem", "ExplorerContainer", "role", "view", "space", "getSpace", "filter", "setFilter", "useState", "model", "useGraphModel", "match", "useGlobalSearch", "builder", "useMemo", "QueryBuilder", "handleChange", "useCallback", "value", "build", "showToolbar", "StackItem", "Content", "toolbar", "Toolbar", "Root", "QueryEditor", "db", "onChange", "D3ForceGraph"]
7
- }
@@ -1,31 +0,0 @@
1
- // src/hooks/useGraphModel.ts
2
- import { useEffect, useState } from "react";
3
- import { SpaceGraphModel } from "@dxos/schema";
4
- var useGraphModel = (space, filter, options, queue) => {
5
- const [model, setModel] = useState(void 0);
6
- useEffect(() => {
7
- if (!space) {
8
- void model?.close();
9
- setModel(void 0);
10
- return;
11
- }
12
- if (!model || model.queue !== queue) {
13
- const model2 = new SpaceGraphModel().setFilter(filter).setOptions(options);
14
- void model2.open(space, queue);
15
- setModel(model2);
16
- } else {
17
- model.setFilter(filter).setOptions(options);
18
- }
19
- }, [
20
- space,
21
- filter,
22
- options,
23
- queue
24
- ]);
25
- return model;
26
- };
27
-
28
- export {
29
- useGraphModel
30
- };
31
- //# sourceMappingURL=chunk-2MKBRIUT.mjs.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/hooks/useGraphModel.ts"],
4
- "sourcesContent": ["//\n// Copyright 2023 DXOS.org\n//\n\nimport { useEffect, useState } from 'react';\n\nimport { type Filter, type Queue, type Space } from '@dxos/client/echo';\nimport { SpaceGraphModel, type SpaceGraphModelOptions } from '@dxos/schema';\n\n// TODO(burdon): Factor out.\nexport const useGraphModel = (\n space: Space | undefined,\n filter?: Filter.Any | undefined,\n options?: SpaceGraphModelOptions,\n queue?: Queue,\n): SpaceGraphModel | undefined => {\n const [model, setModel] = useState<SpaceGraphModel | undefined>(undefined);\n useEffect(() => {\n if (!space) {\n void model?.close();\n setModel(undefined);\n return;\n }\n\n // TODO(burdon): Does this need to be a dependency?\n if (!model || model.queue !== queue) {\n const model = new SpaceGraphModel().setFilter(filter).setOptions(options);\n void model.open(space, queue);\n setModel(model);\n } else {\n model.setFilter(filter).setOptions(options);\n }\n }, [space, filter, options, queue]);\n\n return model;\n};\n"],
5
- "mappings": ";AAIA,SAASA,WAAWC,gBAAgB;AAGpC,SAASC,uBAAoD;AAGtD,IAAMC,gBAAgB,CAC3BC,OACAC,QACAC,SACAC,UAAAA;AAEA,QAAM,CAACC,OAAOC,QAAAA,IAAYC,SAAsCC,MAAAA;AAChEC,YAAU,MAAA;AACR,QAAI,CAACR,OAAO;AACV,WAAKI,OAAOK,MAAAA;AACZJ,eAASE,MAAAA;AACT;IACF;AAGA,QAAI,CAACH,SAASA,MAAMD,UAAUA,OAAO;AACnC,YAAMC,SAAQ,IAAIM,gBAAAA,EAAkBC,UAAUV,MAAAA,EAAQW,WAAWV,OAAAA;AACjE,WAAKE,OAAMS,KAAKb,OAAOG,KAAAA;AACvBE,eAASD,MAAAA;IACX,OAAO;AACLA,YAAMO,UAAUV,MAAAA,EAAQW,WAAWV,OAAAA;IACrC;EACF,GAAG;IAACF;IAAOC;IAAQC;IAASC;GAAM;AAElC,SAAOC;AACT;",
6
- "names": ["useEffect", "useState", "SpaceGraphModel", "useGraphModel", "space", "filter", "options", "queue", "model", "setModel", "useState", "undefined", "useEffect", "close", "SpaceGraphModel", "setFilter", "setOptions", "open"]
7
- }