@dxos/plugin-explorer 0.8.3 → 0.8.4-main.1068cf700f
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/browser/ExplorerContainer-46BHUF6R.mjs +45 -0
- package/dist/lib/browser/ExplorerContainer-46BHUF6R.mjs.map +7 -0
- package/dist/lib/browser/chunk-HIFLWHXR.mjs +83 -0
- package/dist/lib/browser/chunk-HIFLWHXR.mjs.map +7 -0
- package/dist/lib/browser/chunk-J5LGTIGS.mjs +10 -0
- package/dist/lib/browser/chunk-J5LGTIGS.mjs.map +7 -0
- package/dist/lib/browser/{chunk-JRKQNHS6.mjs → chunk-JZSBQYJQ.mjs} +152 -405
- package/dist/lib/{node-esm/chunk-IUFYOE44.mjs.map → browser/chunk-JZSBQYJQ.mjs.map} +3 -3
- package/dist/lib/browser/chunk-KIXHZZ2C.mjs +35 -0
- package/dist/lib/browser/chunk-KIXHZZ2C.mjs.map +7 -0
- package/dist/lib/browser/chunk-MGBT2ZFU.mjs +177 -0
- package/dist/lib/browser/chunk-MGBT2ZFU.mjs.map +7 -0
- package/dist/lib/browser/chunk-YNQF4CPY.mjs +24 -0
- package/dist/lib/browser/chunk-YNQF4CPY.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +60 -70
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/meta.mjs +2 -3
- package/dist/lib/browser/react-surface-TPQAT5EI.mjs +36 -0
- package/dist/lib/browser/react-surface-TPQAT5EI.mjs.map +7 -0
- package/dist/lib/browser/types/index.mjs +6 -6
- package/dist/lib/node-esm/ExplorerContainer-OZNG47QB.mjs +46 -0
- package/dist/lib/node-esm/ExplorerContainer-OZNG47QB.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-3OE6TBJI.mjs +84 -0
- package/dist/lib/node-esm/chunk-3OE6TBJI.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-VSORIAHH.mjs → chunk-ASRWO2N5.mjs} +12 -7
- package/dist/lib/node-esm/chunk-ASRWO2N5.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-3CMBLK6W.mjs → chunk-DK77RB6M.mjs} +9 -6
- package/dist/lib/node-esm/chunk-DK77RB6M.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +11 -0
- package/dist/lib/node-esm/chunk-HSLMI22Q.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-IUFYOE44.mjs → chunk-K5BYG7BW.mjs} +152 -405
- package/dist/lib/{browser/chunk-JRKQNHS6.mjs.map → node-esm/chunk-K5BYG7BW.mjs.map} +3 -3
- package/dist/lib/node-esm/chunk-YWJBDETV.mjs +179 -0
- package/dist/lib/node-esm/chunk-YWJBDETV.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +60 -70
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/meta.mjs +2 -3
- package/dist/lib/node-esm/react-surface-CYHGJJDU.mjs +37 -0
- package/dist/lib/node-esm/react-surface-CYHGJJDU.mjs.map +7 -0
- package/dist/lib/node-esm/types/index.mjs +6 -6
- package/dist/types/src/ExplorerPlugin.d.ts +2 -1
- package/dist/types/src/ExplorerPlugin.d.ts.map +1 -1
- package/dist/types/src/capabilities/index.d.ts +1 -2
- package/dist/types/src/capabilities/index.d.ts.map +1 -1
- package/dist/types/src/capabilities/react-surface/index.d.ts +3 -0
- package/dist/types/src/capabilities/react-surface/index.d.ts.map +1 -0
- package/dist/types/src/capabilities/react-surface/react-surface.d.ts +5 -0
- package/dist/types/src/capabilities/react-surface/react-surface.d.ts.map +1 -0
- package/dist/types/src/components/Chart/Chart.d.ts.map +1 -1
- package/dist/types/src/components/Chart/Chart.stories.d.ts +8 -4
- package/dist/types/src/components/Chart/Chart.stories.d.ts.map +1 -1
- package/dist/types/src/components/ExplorerContainer.d.ts +4 -6
- package/dist/types/src/components/ExplorerContainer.d.ts.map +1 -1
- package/dist/types/src/components/Globe/Globe.d.ts.map +1 -1
- package/dist/types/src/components/Globe/Globe.stories.d.ts +8 -4
- package/dist/types/src/components/Globe/Globe.stories.d.ts.map +1 -1
- package/dist/types/src/components/Graph/D3ForceGraph.d.ts +2 -2
- package/dist/types/src/components/Graph/D3ForceGraph.d.ts.map +1 -1
- package/dist/types/src/components/Graph/D3ForceGraph.stories.d.ts +13 -4
- package/dist/types/src/components/Graph/D3ForceGraph.stories.d.ts.map +1 -1
- package/dist/types/src/components/Graph/ForceGraph.stories.d.ts +13 -4
- package/dist/types/src/components/Graph/ForceGraph.stories.d.ts.map +1 -1
- package/dist/types/src/components/Graph/adapter.d.ts +1 -1
- package/dist/types/src/components/Graph/adapter.d.ts.map +1 -1
- package/dist/types/src/components/Graph/testing.d.ts +1 -1
- package/dist/types/src/components/Graph/testing.d.ts.map +1 -1
- package/dist/types/src/components/Tree/Tree.d.ts.map +1 -1
- package/dist/types/src/components/Tree/Tree.stories.d.ts +13 -16
- package/dist/types/src/components/Tree/Tree.stories.d.ts.map +1 -1
- package/dist/types/src/components/Tree/testing/generator.d.ts.map +1 -1
- package/dist/types/src/components/Tree/types/tree.d.ts +18 -16
- package/dist/types/src/components/Tree/types/tree.d.ts.map +1 -1
- package/dist/types/src/components/Tree/types/types.d.ts +1 -1
- package/dist/types/src/components/Tree/types/types.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +2 -4
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useGraphModel.d.ts +2 -2
- package/dist/types/src/hooks/useGraphModel.d.ts.map +1 -1
- package/dist/types/src/meta.d.ts +2 -3
- package/dist/types/src/meta.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +34 -22
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types/ExplorerAction.d.ts +6 -0
- package/dist/types/src/types/ExplorerAction.d.ts.map +1 -0
- package/dist/types/src/types/Graph.d.ts +30 -0
- package/dist/types/src/types/Graph.d.ts.map +1 -0
- package/dist/types/src/types/index.d.ts +2 -2
- package/dist/types/src/types/index.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +61 -44
- package/src/ExplorerPlugin.tsx +31 -53
- package/src/capabilities/index.ts +1 -4
- package/src/capabilities/react-surface/index.ts +7 -0
- package/src/capabilities/react-surface/react-surface.tsx +31 -0
- package/src/components/Chart/Chart.stories.tsx +10 -8
- package/src/components/Chart/Chart.tsx +1 -1
- package/src/components/ExplorerContainer.tsx +30 -14
- package/src/components/Globe/Globe.stories.tsx +15 -13
- package/src/components/Globe/Globe.tsx +1 -1
- package/src/components/Graph/D3ForceGraph.stories.tsx +40 -21
- package/src/components/Graph/D3ForceGraph.tsx +6 -6
- package/src/components/Graph/ForceGraph.stories.tsx +40 -21
- package/src/components/Graph/ForceGraph.tsx +5 -5
- package/src/components/Graph/adapter.ts +14 -8
- package/src/components/Graph/testing.ts +13 -10
- package/src/components/Tree/Tree.stories.tsx +29 -22
- package/src/components/Tree/Tree.tsx +11 -6
- package/src/components/Tree/testing/generator.ts +4 -2
- package/src/components/Tree/types/tree.test.ts +8 -5
- package/src/components/Tree/types/tree.ts +40 -19
- package/src/components/Tree/types/types.ts +1 -1
- package/src/components/index.ts +3 -3
- package/src/hooks/useGraphModel.ts +9 -5
- package/src/meta.ts +9 -6
- package/src/translations.ts +12 -6
- package/src/types/ExplorerAction.ts +21 -0
- package/src/types/Graph.ts +62 -0
- package/src/types/index.ts +2 -2
- package/dist/lib/browser/ExplorerContainer-DXL34I3F.mjs +0 -37
- package/dist/lib/browser/ExplorerContainer-DXL34I3F.mjs.map +0 -7
- package/dist/lib/browser/chunk-73YTQHOT.mjs +0 -38
- package/dist/lib/browser/chunk-73YTQHOT.mjs.map +0 -7
- package/dist/lib/browser/chunk-OBAFAA5V.mjs +0 -21
- package/dist/lib/browser/chunk-OBAFAA5V.mjs.map +0 -7
- package/dist/lib/browser/chunk-SLB2F5AO.mjs +0 -30
- package/dist/lib/browser/chunk-SLB2F5AO.mjs.map +0 -7
- package/dist/lib/browser/chunk-Z5BGAHLD.mjs +0 -187
- package/dist/lib/browser/chunk-Z5BGAHLD.mjs.map +0 -7
- package/dist/lib/browser/intent-resolver-JZKYVFQJ.mjs +0 -24
- package/dist/lib/browser/intent-resolver-JZKYVFQJ.mjs.map +0 -7
- package/dist/lib/browser/react-surface-IAEP2GBT.mjs +0 -31
- package/dist/lib/browser/react-surface-IAEP2GBT.mjs.map +0 -7
- package/dist/lib/node/ExplorerContainer-VUXH55VV.cjs +0 -61
- package/dist/lib/node/ExplorerContainer-VUXH55VV.cjs.map +0 -7
- package/dist/lib/node/chunk-4T4LCT5R.cjs +0 -52
- package/dist/lib/node/chunk-4T4LCT5R.cjs.map +0 -7
- package/dist/lib/node/chunk-BCDVG2CH.cjs +0 -44
- package/dist/lib/node/chunk-BCDVG2CH.cjs.map +0 -7
- package/dist/lib/node/chunk-MLRYW4WQ.cjs +0 -56
- package/dist/lib/node/chunk-MLRYW4WQ.cjs.map +0 -7
- package/dist/lib/node/chunk-SVU4VMYX.cjs +0 -11339
- package/dist/lib/node/chunk-SVU4VMYX.cjs.map +0 -7
- package/dist/lib/node/chunk-TY543HPV.cjs +0 -214
- package/dist/lib/node/chunk-TY543HPV.cjs.map +0 -7
- package/dist/lib/node/index.cjs +0 -130
- package/dist/lib/node/index.cjs.map +0 -7
- package/dist/lib/node/intent-resolver-G2MFNIXA.cjs +0 -39
- package/dist/lib/node/intent-resolver-G2MFNIXA.cjs.map +0 -7
- package/dist/lib/node/meta.cjs +0 -31
- package/dist/lib/node/meta.cjs.map +0 -7
- package/dist/lib/node/meta.json +0 -1
- package/dist/lib/node/react-surface-UJD5RGRZ.cjs +0 -53
- package/dist/lib/node/react-surface-UJD5RGRZ.cjs.map +0 -7
- package/dist/lib/node/types/index.cjs +0 -32
- package/dist/lib/node/types/index.cjs.map +0 -7
- package/dist/lib/node-esm/ExplorerContainer-MFE7PXF4.mjs +0 -38
- package/dist/lib/node-esm/ExplorerContainer-MFE7PXF4.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-3CMBLK6W.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-AE7VHUJM.mjs +0 -189
- package/dist/lib/node-esm/chunk-AE7VHUJM.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-N6VEANUZ.mjs +0 -39
- package/dist/lib/node-esm/chunk-N6VEANUZ.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-VSORIAHH.mjs.map +0 -7
- package/dist/lib/node-esm/intent-resolver-7G6ZKM6E.mjs +0 -25
- package/dist/lib/node-esm/intent-resolver-7G6ZKM6E.mjs.map +0 -7
- package/dist/lib/node-esm/react-surface-XBH3WZDL.mjs +0 -32
- package/dist/lib/node-esm/react-surface-XBH3WZDL.mjs.map +0 -7
- package/dist/types/src/capabilities/intent-resolver.d.ts +0 -4
- package/dist/types/src/capabilities/intent-resolver.d.ts.map +0 -1
- package/dist/types/src/capabilities/react-surface.d.ts +0 -4
- package/dist/types/src/capabilities/react-surface.d.ts.map +0 -1
- package/dist/types/src/types/schema.d.ts +0 -12
- package/dist/types/src/types/schema.d.ts.map +0 -1
- package/dist/types/src/types/types.d.ts +0 -18
- package/dist/types/src/types/types.d.ts.map +0 -1
- package/src/capabilities/intent-resolver.ts +0 -19
- package/src/capabilities/react-surface.tsx +0 -23
- package/src/types/schema.ts +0 -16
- package/src/types/types.ts +0 -21
|
@@ -2,24 +2,27 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import '@
|
|
5
|
+
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
6
|
+
import React, { useState } from 'react';
|
|
6
7
|
|
|
7
|
-
import {
|
|
8
|
-
import React, { useEffect, useState } from 'react';
|
|
9
|
-
|
|
10
|
-
import { Obj } from '@dxos/echo';
|
|
8
|
+
import { Type } from '@dxos/echo';
|
|
11
9
|
import { faker } from '@dxos/random';
|
|
12
10
|
import { useClient } from '@dxos/react-client';
|
|
13
11
|
import { type Space } from '@dxos/react-client/echo';
|
|
14
12
|
import { withClientProvider } from '@dxos/react-client/testing';
|
|
15
|
-
import {
|
|
13
|
+
import { useAsyncEffect } from '@dxos/react-ui';
|
|
14
|
+
import { withLayout, withTheme } from '@dxos/react-ui/testing';
|
|
15
|
+
import { View } from '@dxos/schema';
|
|
16
16
|
import { type ValueGenerator } from '@dxos/schema/testing';
|
|
17
|
-
import {
|
|
17
|
+
import { withRegistry } from '@dxos/storybook-utils';
|
|
18
|
+
import { render } from '@dxos/storybook-utils';
|
|
19
|
+
import { HasRelationship, Organization, Person, Pipeline } from '@dxos/types';
|
|
20
|
+
|
|
21
|
+
import { useGraphModel } from '../../hooks';
|
|
22
|
+
import { Graph } from '../../types';
|
|
18
23
|
|
|
19
24
|
import { ForceGraph } from './ForceGraph';
|
|
20
25
|
import { generate } from './testing';
|
|
21
|
-
import { useGraphModel } from '../../hooks';
|
|
22
|
-
import { ViewType } from '../../types';
|
|
23
26
|
|
|
24
27
|
const generator = faker as any as ValueGenerator;
|
|
25
28
|
|
|
@@ -28,37 +31,53 @@ faker.seed(1);
|
|
|
28
31
|
const DefaultStory = () => {
|
|
29
32
|
const client = useClient();
|
|
30
33
|
const [space, setSpace] = useState<Space>();
|
|
31
|
-
const [
|
|
32
|
-
|
|
34
|
+
const [graph, setGraph] = useState<Graph.Graph>();
|
|
35
|
+
|
|
36
|
+
useAsyncEffect(async () => {
|
|
33
37
|
const space = client.spaces.default;
|
|
34
38
|
void generate(space, generator);
|
|
35
|
-
const view =
|
|
39
|
+
const { view } = await View.makeFromDatabase({ db: space.db, typename: Type.getTypename(Graph.Graph) });
|
|
40
|
+
const graph = Graph.make({ name: 'Test', view });
|
|
41
|
+
space.db.add(graph);
|
|
36
42
|
setSpace(space);
|
|
37
|
-
|
|
38
|
-
}, []);
|
|
43
|
+
setGraph(graph);
|
|
44
|
+
}, [client]);
|
|
39
45
|
|
|
40
46
|
const model = useGraphModel(space);
|
|
41
|
-
if (!model || !space || !
|
|
47
|
+
if (!model || !space || !graph) {
|
|
42
48
|
return null;
|
|
43
49
|
}
|
|
44
50
|
|
|
45
51
|
return <ForceGraph model={model} />;
|
|
46
52
|
};
|
|
47
53
|
|
|
48
|
-
const meta
|
|
54
|
+
const meta = {
|
|
49
55
|
title: 'plugins/plugin-explorer/ForceGraph',
|
|
50
56
|
component: ForceGraph,
|
|
51
57
|
render: render(DefaultStory),
|
|
52
58
|
decorators: [
|
|
59
|
+
withRegistry,
|
|
60
|
+
withTheme(),
|
|
61
|
+
withLayout(),
|
|
53
62
|
withClientProvider({
|
|
54
63
|
createSpace: true,
|
|
55
|
-
types: [
|
|
64
|
+
types: [
|
|
65
|
+
Graph.Graph,
|
|
66
|
+
View.View,
|
|
67
|
+
HasRelationship.HasRelationship,
|
|
68
|
+
Organization.Organization,
|
|
69
|
+
Pipeline.Pipeline,
|
|
70
|
+
Person.Person,
|
|
71
|
+
],
|
|
56
72
|
}),
|
|
57
|
-
withTheme,
|
|
58
|
-
withLayout({ fullscreen: true }),
|
|
59
73
|
],
|
|
60
|
-
|
|
74
|
+
parameters: {
|
|
75
|
+
layout: 'fullscreen',
|
|
76
|
+
},
|
|
77
|
+
} satisfies Meta<typeof ForceGraph>;
|
|
61
78
|
|
|
62
79
|
export default meta;
|
|
63
80
|
|
|
64
|
-
|
|
81
|
+
type Story = StoryObj<typeof meta>;
|
|
82
|
+
|
|
83
|
+
export const Default: Story = {};
|
|
@@ -7,7 +7,7 @@ import NativeForceGraph from 'force-graph';
|
|
|
7
7
|
import React, { type FC, useEffect, useRef, useState } from 'react';
|
|
8
8
|
import { useResizeDetector } from 'react-resize-detector';
|
|
9
9
|
|
|
10
|
-
import {
|
|
10
|
+
import { type SearchResult, filterObjectsSync } from '@dxos/plugin-search';
|
|
11
11
|
import { type SpaceGraphModel } from '@dxos/schema';
|
|
12
12
|
|
|
13
13
|
import { GraphAdapter } from './adapter';
|
|
@@ -20,9 +20,9 @@ export type ForceGraphProps = {
|
|
|
20
20
|
export const ForceGraph: FC<ForceGraphProps> = ({ model, match }) => {
|
|
21
21
|
const { ref, width, height } = useResizeDetector({ refreshRate: 200 });
|
|
22
22
|
const rootRef = useRef<HTMLDivElement>(null);
|
|
23
|
-
const forceGraph = useRef<NativeForceGraph>();
|
|
23
|
+
const forceGraph = useRef<NativeForceGraph>(null);
|
|
24
24
|
|
|
25
|
-
const filteredRef = useRef<SearchResult[]>();
|
|
25
|
+
const filteredRef = useRef<SearchResult[]>([]);
|
|
26
26
|
filteredRef.current = filterObjectsSync(model?.objects ?? [], match);
|
|
27
27
|
|
|
28
28
|
const [data, setData] = useState<GraphAdapter>();
|
|
@@ -39,7 +39,7 @@ export const ForceGraph: FC<ForceGraphProps> = ({ model, match }) => {
|
|
|
39
39
|
forceGraph.current = new NativeForceGraph(rootRef.current)
|
|
40
40
|
// https://github.com/vasturiano/force-graph?tab=readme-ov-file#node-styling
|
|
41
41
|
.nodeRelSize(6)
|
|
42
|
-
.nodeLabel((node: any) => (node.type === 'schema' ? node.data.typename : node.data.label ?? node.id))
|
|
42
|
+
.nodeLabel((node: any) => (node.type === 'schema' ? node.data.typename : (node.data.label ?? node.id)))
|
|
43
43
|
.nodeAutoColorBy((node: any) => (node.type === 'schema' ? 'schema' : node.data.typename))
|
|
44
44
|
|
|
45
45
|
// https://github.com/vasturiano/force-graph?tab=readme-ov-file#link-styling
|
|
@@ -48,7 +48,7 @@ export const ForceGraph: FC<ForceGraphProps> = ({ model, match }) => {
|
|
|
48
48
|
|
|
49
49
|
return () => {
|
|
50
50
|
forceGraph.current?.pauseAnimation().graphData({ nodes: [], links: [] });
|
|
51
|
-
forceGraph.current =
|
|
51
|
+
forceGraph.current = null;
|
|
52
52
|
};
|
|
53
53
|
}, []);
|
|
54
54
|
|
|
@@ -25,19 +25,25 @@ export class GraphAdapter implements GraphData {
|
|
|
25
25
|
private readonly _nodes: GraphNode[] = [];
|
|
26
26
|
private readonly _links: GraphLink[] = [];
|
|
27
27
|
|
|
28
|
-
constructor(private readonly graph: Graph) {
|
|
29
|
-
this._nodes = graph.nodes.map((node) => ({
|
|
28
|
+
constructor(private readonly graph: Graph.Any) {
|
|
29
|
+
this._nodes = graph.nodes.map((node: Graph.Node.Any) => ({
|
|
30
30
|
id: node.id,
|
|
31
31
|
type: node.type,
|
|
32
32
|
data: node.data,
|
|
33
33
|
}));
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
35
|
+
// Build a set of node IDs for efficient lookup.
|
|
36
|
+
const nodeIds = new Set(this._nodes.map((node) => node.id));
|
|
37
|
+
|
|
38
|
+
// Filter out edges where source or target node doesn't exist.
|
|
39
|
+
this._links = graph.edges
|
|
40
|
+
.filter((edge: Graph.Edge.Any) => nodeIds.has(edge.source) && nodeIds.has(edge.target))
|
|
41
|
+
.map((edge: Graph.Edge.Any) => ({
|
|
42
|
+
type: edge.type,
|
|
43
|
+
source: edge.source,
|
|
44
|
+
target: edge.target,
|
|
45
|
+
data: edge.data,
|
|
46
|
+
}));
|
|
41
47
|
}
|
|
42
48
|
|
|
43
49
|
get nodes() {
|
|
@@ -3,17 +3,17 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { type Space } from '@dxos/client/echo';
|
|
6
|
-
import { Query, Relation
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
6
|
+
import { type Obj, Query, Relation } from '@dxos/echo';
|
|
7
|
+
import { type TypeSpec, type ValueGenerator, createObjectFactory } from '@dxos/schema/testing';
|
|
8
|
+
import { HasRelationship, Organization, Person, Pipeline } from '@dxos/types';
|
|
9
9
|
import { range } from '@dxos/util';
|
|
10
10
|
|
|
11
|
-
const getObject = (objects: Obj.
|
|
11
|
+
const getObject = (objects: Obj.Unknown[]) => objects[Math.floor(Math.random() * objects.length)];
|
|
12
12
|
|
|
13
13
|
const defaultTypes: TypeSpec[] = [
|
|
14
|
-
{ type:
|
|
15
|
-
{ type:
|
|
16
|
-
{ type:
|
|
14
|
+
{ type: Organization.Organization, count: 5 },
|
|
15
|
+
{ type: Pipeline.Pipeline, count: 5 },
|
|
16
|
+
{ type: Person.Person, count: 10 },
|
|
17
17
|
];
|
|
18
18
|
|
|
19
19
|
export type GenerateOptions = {
|
|
@@ -24,7 +24,10 @@ export type GenerateOptions = {
|
|
|
24
24
|
};
|
|
25
25
|
};
|
|
26
26
|
|
|
27
|
-
const defaultRelations: GenerateOptions['relations'] = {
|
|
27
|
+
const defaultRelations: GenerateOptions['relations'] = {
|
|
28
|
+
kind: 'friend',
|
|
29
|
+
count: 10,
|
|
30
|
+
};
|
|
28
31
|
|
|
29
32
|
/**
|
|
30
33
|
* @deprecated Use @dxos/schema.
|
|
@@ -38,13 +41,13 @@ export const generate = async (
|
|
|
38
41
|
await createObjects(spec);
|
|
39
42
|
|
|
40
43
|
// Add relations between objects.
|
|
41
|
-
const
|
|
44
|
+
const contacts = await space.db.query(Query.type(Person.Person)).run();
|
|
42
45
|
for (const _ of range(relations.count)) {
|
|
43
46
|
const source = getObject(contacts);
|
|
44
47
|
const target = getObject(contacts);
|
|
45
48
|
if (source.id !== target.id) {
|
|
46
49
|
space.db.add(
|
|
47
|
-
Relation.make(
|
|
50
|
+
Relation.make(HasRelationship.HasRelationship, {
|
|
48
51
|
[Relation.Source]: source,
|
|
49
52
|
[Relation.Target]: target,
|
|
50
53
|
kind: relations.kind,
|
|
@@ -2,25 +2,26 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import '@
|
|
6
|
-
|
|
7
|
-
import { type Meta } from '@storybook/react';
|
|
8
|
-
import React, { type FC, useEffect, useState } from 'react';
|
|
5
|
+
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
6
|
+
import React, { useEffect, useState } from 'react';
|
|
9
7
|
|
|
10
8
|
import { faker } from '@dxos/random';
|
|
11
9
|
import { useClient } from '@dxos/react-client';
|
|
12
10
|
import { type ClientRepeatedComponentProps, ClientRepeater } from '@dxos/react-client/testing';
|
|
13
|
-
import { withLayout, withTheme } from '@dxos/
|
|
11
|
+
import { withLayout, withTheme } from '@dxos/react-ui/testing';
|
|
12
|
+
import { withRegistry } from '@dxos/storybook-utils';
|
|
14
13
|
|
|
15
14
|
import { Tree, type TreeComponentProps } from './Tree';
|
|
16
|
-
import {
|
|
15
|
+
import { Tree as TreeModel, TreeType } from './types';
|
|
17
16
|
|
|
18
17
|
// TODO(burdon): Storybook for Graph/Tree/Plot (generics); incl. GraphModel.
|
|
19
18
|
// TODO(burdon): Type for all Explorer components (Space, Object, Query, etc.) incl.
|
|
20
19
|
|
|
21
20
|
faker.seed(1);
|
|
22
21
|
|
|
23
|
-
|
|
22
|
+
type ComponentProps = ClientRepeatedComponentProps & { type?: TreeComponentProps<any>['variant'] };
|
|
23
|
+
|
|
24
|
+
const Component = ({ type }: ComponentProps) => {
|
|
24
25
|
const client = useClient();
|
|
25
26
|
const space = client.spaces.default;
|
|
26
27
|
const [object, setObject] = useState<TreeType>();
|
|
@@ -38,32 +39,38 @@ const Story: FC<ClientRepeatedComponentProps & { type?: TreeComponentProps<any>[
|
|
|
38
39
|
return <Tree space={space} selected={object?.id} variant={type} />;
|
|
39
40
|
};
|
|
40
41
|
|
|
41
|
-
|
|
42
|
+
const DefaultStory = () => {
|
|
43
|
+
return <ClientRepeater component={Component} types={[TreeType]} createSpace />;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const meta = {
|
|
47
|
+
title: 'plugins/plugin-explorer/Tree',
|
|
48
|
+
component: Tree as any,
|
|
49
|
+
render: DefaultStory,
|
|
50
|
+
decorators: [withRegistry, withTheme(), withLayout()],
|
|
51
|
+
parameters: {
|
|
52
|
+
layout: 'fullscreen',
|
|
53
|
+
},
|
|
54
|
+
} satisfies Meta<typeof DefaultStory>;
|
|
55
|
+
|
|
56
|
+
export default meta;
|
|
57
|
+
|
|
58
|
+
type Story = StoryObj<typeof meta>;
|
|
59
|
+
|
|
60
|
+
export const Tidy: Story = {
|
|
42
61
|
args: {
|
|
43
62
|
type: 'tidy',
|
|
44
63
|
},
|
|
45
64
|
};
|
|
46
65
|
|
|
47
|
-
export const Radial = {
|
|
66
|
+
export const Radial: Story = {
|
|
48
67
|
args: {
|
|
49
68
|
type: 'radial',
|
|
50
69
|
},
|
|
51
70
|
};
|
|
52
71
|
|
|
53
|
-
export const Edge = {
|
|
72
|
+
export const Edge: Story = {
|
|
54
73
|
args: {
|
|
55
74
|
type: 'edge',
|
|
56
75
|
},
|
|
57
76
|
};
|
|
58
|
-
|
|
59
|
-
const meta: Meta = {
|
|
60
|
-
title: 'plugins/plugin-explorer/Tree',
|
|
61
|
-
component: Tree,
|
|
62
|
-
render: () => <ClientRepeater component={Story} types={[TreeType]} createSpace />,
|
|
63
|
-
decorators: [withTheme, withLayout({ fullscreen: true })],
|
|
64
|
-
parameters: {
|
|
65
|
-
layout: 'fullscreen',
|
|
66
|
-
},
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
export default meta;
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import { RegistryContext } from '@effect-atom/atom-react';
|
|
6
|
+
import React, { useContext, useEffect, useRef, useState } from 'react';
|
|
6
7
|
|
|
7
8
|
import { type Space } from '@dxos/client/echo';
|
|
8
9
|
import { useAsyncState } from '@dxos/react-ui';
|
|
@@ -10,7 +11,7 @@ import { SVG, type SVGContext } from '@dxos/react-ui-graph';
|
|
|
10
11
|
import { SpaceGraphModel } from '@dxos/schema';
|
|
11
12
|
|
|
12
13
|
import { HierarchicalEdgeBundling, RadialTree, TidyTree } from './layout';
|
|
13
|
-
import {
|
|
14
|
+
import { type TreeNode, mapGraphToTreeData } from './types';
|
|
14
15
|
|
|
15
16
|
// TODO(burdon): Create dge bundling graph using d3.hierarchy.
|
|
16
17
|
// https://observablehq.com/@d3/hierarchical-edge-bundling?intent=fork
|
|
@@ -62,7 +63,11 @@ export type TreeComponentProps<N = unknown> = {
|
|
|
62
63
|
|
|
63
64
|
// TODO(burdon): Label accessor.
|
|
64
65
|
export const Tree = <N,>({ space, selected, variant = 'tidy', onNodeClick }: TreeComponentProps<N>) => {
|
|
65
|
-
const
|
|
66
|
+
const registry = useContext(RegistryContext);
|
|
67
|
+
const [model] = useAsyncState(
|
|
68
|
+
async () => (space ? new SpaceGraphModel(registry).open(space.db) : undefined),
|
|
69
|
+
[space, selected, registry],
|
|
70
|
+
);
|
|
66
71
|
|
|
67
72
|
const [tree, setTree] = useState<TreeNode>();
|
|
68
73
|
useEffect(() => {
|
|
@@ -75,8 +80,8 @@ export const Tree = <N,>({ space, selected, variant = 'tidy', onNodeClick }: Tre
|
|
|
75
80
|
const context = useRef<SVGContext>(null);
|
|
76
81
|
|
|
77
82
|
useEffect(() => {
|
|
78
|
-
if (context.current) {
|
|
79
|
-
const { width, height } = context.current.size
|
|
83
|
+
if (context.current?.size) {
|
|
84
|
+
const { width, height } = context.current.size;
|
|
80
85
|
const size = Math.min(width, height);
|
|
81
86
|
const radius = size * 0.4;
|
|
82
87
|
const options = {
|
|
@@ -100,7 +105,7 @@ export const Tree = <N,>({ space, selected, variant = 'tidy', onNodeClick }: Tre
|
|
|
100
105
|
}, [context.current, tree]);
|
|
101
106
|
|
|
102
107
|
return (
|
|
103
|
-
<div onClick={() => onNodeClick?.()}>
|
|
108
|
+
<div className='grow' onClick={() => onNodeClick?.()}>
|
|
104
109
|
<SVG.Root ref={context} />
|
|
105
110
|
</div>
|
|
106
111
|
);
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { Key } from '@dxos/echo';
|
|
5
|
+
import { Key, Obj } from '@dxos/echo';
|
|
6
6
|
import { range } from '@dxos/util';
|
|
7
7
|
|
|
8
8
|
import { Tree, type TreeNodeType } from '../types';
|
|
@@ -16,7 +16,9 @@ const random = (min: number, max: number) => Math.floor(Math.random() * (max - m
|
|
|
16
16
|
*/
|
|
17
17
|
export const createTree = (spec: NumberOrNumberArray[] = [], createText?: () => string): Tree => {
|
|
18
18
|
const tree = new Tree();
|
|
19
|
-
tree.
|
|
19
|
+
Obj.change(tree.tree, () => {
|
|
20
|
+
tree.root.data = { text: 'root' };
|
|
21
|
+
});
|
|
20
22
|
|
|
21
23
|
const createNodes = (parent: TreeNodeType, spec: NumberOrNumberArray = 0): TreeNodeType[] => {
|
|
22
24
|
const count = Array.isArray(spec) ? random(spec[0], spec[1]) : spec;
|
|
@@ -6,11 +6,12 @@ import { describe, test } from 'vitest';
|
|
|
6
6
|
|
|
7
7
|
import { Obj, Ref } from '@dxos/echo';
|
|
8
8
|
import { faker } from '@dxos/random';
|
|
9
|
-
import {
|
|
9
|
+
import { Task } from '@dxos/types';
|
|
10
10
|
|
|
11
|
-
import { type Tree } from './tree';
|
|
12
11
|
import { createTree } from '../testing';
|
|
13
12
|
|
|
13
|
+
import { type Tree } from './tree';
|
|
14
|
+
|
|
14
15
|
faker.seed(0);
|
|
15
16
|
|
|
16
17
|
const print = (tree: Tree) => {
|
|
@@ -123,11 +124,13 @@ describe('tree', () => {
|
|
|
123
124
|
});
|
|
124
125
|
|
|
125
126
|
test('task', ({ expect }) => {
|
|
126
|
-
const task = Obj.make(
|
|
127
|
-
expect(task.
|
|
127
|
+
const task = Obj.make(Task.Task, { title: 'Test task.' });
|
|
128
|
+
expect(task.title).to.eq('Test task.');
|
|
128
129
|
|
|
129
130
|
const tree = createTree();
|
|
130
131
|
const node = tree.addNode(tree.root);
|
|
131
|
-
|
|
132
|
+
Obj.change(tree.tree, () => {
|
|
133
|
+
node.ref = Ref.make(task);
|
|
134
|
+
});
|
|
132
135
|
});
|
|
133
136
|
});
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import * as Schema from 'effect/Schema';
|
|
6
6
|
|
|
7
7
|
import { Key, Obj, Type } from '@dxos/echo';
|
|
8
|
+
import { TestSchema } from '@dxos/echo/testing';
|
|
8
9
|
import { invariant } from '@dxos/invariant';
|
|
9
10
|
|
|
10
11
|
// TODO(burdon): Reconcile with @dxos/graph (i.e., common types).
|
|
@@ -13,7 +14,7 @@ export const TreeNodeType = Schema.Struct({
|
|
|
13
14
|
id: Key.ObjectId,
|
|
14
15
|
children: Schema.mutable(Schema.Array(Key.ObjectId)),
|
|
15
16
|
data: Schema.mutable(Schema.Record({ key: Schema.String, value: Schema.Any })),
|
|
16
|
-
ref: Schema.optional(Type.Ref(
|
|
17
|
+
ref: Schema.optional(Type.Ref(TestSchema.Expando)),
|
|
17
18
|
}).pipe(Schema.mutable);
|
|
18
19
|
|
|
19
20
|
export interface TreeNodeType extends Schema.Schema.Type<typeof TreeNodeType> {}
|
|
@@ -22,7 +23,7 @@ export const TreeType = Schema.Struct({
|
|
|
22
23
|
root: Key.ObjectId,
|
|
23
24
|
nodes: Schema.mutable(Schema.Record({ key: Key.ObjectId, value: TreeNodeType })),
|
|
24
25
|
}).pipe(
|
|
25
|
-
Type.
|
|
26
|
+
Type.object({
|
|
26
27
|
typename: 'dxos.org/type/Tree',
|
|
27
28
|
version: '0.1.0',
|
|
28
29
|
}),
|
|
@@ -184,9 +185,11 @@ export class Tree {
|
|
|
184
185
|
clear(): void {
|
|
185
186
|
const root = this._tree.nodes[this._tree.root];
|
|
186
187
|
root.children.length = 0;
|
|
187
|
-
this._tree
|
|
188
|
-
|
|
189
|
-
|
|
188
|
+
Obj.change(this._tree, (t) => {
|
|
189
|
+
t.nodes = {
|
|
190
|
+
[root.id]: root,
|
|
191
|
+
};
|
|
192
|
+
});
|
|
190
193
|
}
|
|
191
194
|
|
|
192
195
|
/**
|
|
@@ -198,8 +201,11 @@ export class Tree {
|
|
|
198
201
|
node = { id, children: [], data: { text: '' } }; // TODO(burdon): Generic.
|
|
199
202
|
}
|
|
200
203
|
|
|
201
|
-
|
|
202
|
-
|
|
204
|
+
const nodeToAdd = node;
|
|
205
|
+
Obj.change(this._tree, (t) => {
|
|
206
|
+
t.nodes[nodeToAdd.id] = nodeToAdd;
|
|
207
|
+
parent.children.splice(index ?? parent.children.length, 0, nodeToAdd.id);
|
|
208
|
+
});
|
|
203
209
|
return node;
|
|
204
210
|
}
|
|
205
211
|
|
|
@@ -212,10 +218,14 @@ export class Tree {
|
|
|
212
218
|
return undefined;
|
|
213
219
|
}
|
|
214
220
|
|
|
215
|
-
|
|
221
|
+
Obj.change(this._tree, (t) => {
|
|
222
|
+
delete t.nodes[node.id];
|
|
223
|
+
});
|
|
216
224
|
const idx = parent.children.findIndex((child) => child === id);
|
|
217
225
|
if (idx !== -1) {
|
|
218
|
-
|
|
226
|
+
Obj.change(this._tree, () => {
|
|
227
|
+
parent.children.splice(idx, 1);
|
|
228
|
+
});
|
|
219
229
|
}
|
|
220
230
|
|
|
221
231
|
return node;
|
|
@@ -232,8 +242,10 @@ export class Tree {
|
|
|
232
242
|
}
|
|
233
243
|
|
|
234
244
|
const child = node.children[from];
|
|
235
|
-
|
|
236
|
-
|
|
245
|
+
Obj.change(this._tree, () => {
|
|
246
|
+
node.children.splice(from, 1);
|
|
247
|
+
node.children.splice(to, 0, child);
|
|
248
|
+
});
|
|
237
249
|
return this.getNode(child);
|
|
238
250
|
}
|
|
239
251
|
|
|
@@ -252,8 +264,10 @@ export class Tree {
|
|
|
252
264
|
}
|
|
253
265
|
|
|
254
266
|
const previous = this.getNode(parent.children[idx - 1]);
|
|
255
|
-
|
|
256
|
-
|
|
267
|
+
Obj.change(this._tree, () => {
|
|
268
|
+
parent.children.splice(idx, 1);
|
|
269
|
+
previous.children.push(node.id);
|
|
270
|
+
});
|
|
257
271
|
}
|
|
258
272
|
|
|
259
273
|
/**
|
|
@@ -270,16 +284,23 @@ export class Tree {
|
|
|
270
284
|
return;
|
|
271
285
|
}
|
|
272
286
|
|
|
273
|
-
// Remove node from parent.
|
|
287
|
+
// Remove node from parent and get following siblings.
|
|
274
288
|
const nodeIdx = parent.children.findIndex((id) => id === node.id);
|
|
275
|
-
|
|
276
|
-
|
|
289
|
+
let rest: Key.ObjectId[] = [];
|
|
290
|
+
Obj.change(this._tree, () => {
|
|
291
|
+
const removed = parent.children.splice(nodeIdx, parent.children.length - nodeIdx);
|
|
292
|
+
rest = removed.slice(1); // Skip the node itself.
|
|
293
|
+
});
|
|
277
294
|
|
|
278
295
|
// Add to ancestor.
|
|
279
296
|
const parentIdx = this.getChildNodes(ancestor).findIndex((n) => n.id === parent.id);
|
|
280
|
-
|
|
297
|
+
Obj.change(this._tree, () => {
|
|
298
|
+
ancestor.children.splice(parentIdx + 1, 0, node.id);
|
|
299
|
+
});
|
|
281
300
|
|
|
282
301
|
// Transplant following siblings to current node.
|
|
283
|
-
|
|
302
|
+
Obj.change(this._tree, () => {
|
|
303
|
+
node.children.push(...rest);
|
|
304
|
+
});
|
|
284
305
|
}
|
|
285
306
|
}
|
|
@@ -10,7 +10,7 @@ export type TreeNode = {
|
|
|
10
10
|
children?: TreeNode[];
|
|
11
11
|
};
|
|
12
12
|
|
|
13
|
-
export const mapGraphToTreeData = (model: GraphModel, maxDepth = 8): TreeNode | undefined => {
|
|
13
|
+
export const mapGraphToTreeData = (model: GraphModel.GraphModel, maxDepth = 8): TreeNode | undefined => {
|
|
14
14
|
// TODO(burdon): Convert to common/graph.
|
|
15
15
|
// const mapNode = (node: N, depth = 0): TreeNode => {
|
|
16
16
|
// const treeNode: TreeNode = {
|
package/src/components/index.ts
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { lazy } from 'react';
|
|
6
|
-
|
|
7
|
-
export const ExplorerContainer = lazy(() => import('./ExplorerContainer'));
|
|
5
|
+
import { type ComponentType, lazy } from 'react';
|
|
8
6
|
|
|
9
7
|
export * from './Chart';
|
|
10
8
|
export * from './Globe';
|
|
11
9
|
export * from './Graph';
|
|
12
10
|
export * from './Tree';
|
|
11
|
+
|
|
12
|
+
export const ExplorerContainer: ComponentType<any> = lazy(() => import('./ExplorerContainer'));
|
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
import { useEffect, useState } from 'react';
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { Capabilities } from '@dxos/app-framework';
|
|
8
|
+
import { useCapability } from '@dxos/app-framework/ui';
|
|
9
|
+
import { type Filter, type Queue, type Space } from '@dxos/client/echo';
|
|
8
10
|
import { SpaceGraphModel, type SpaceGraphModelOptions } from '@dxos/schema';
|
|
9
11
|
|
|
10
12
|
// TODO(burdon): Factor out.
|
|
@@ -12,7 +14,9 @@ export const useGraphModel = (
|
|
|
12
14
|
space: Space | undefined,
|
|
13
15
|
filter?: Filter.Any | undefined,
|
|
14
16
|
options?: SpaceGraphModelOptions,
|
|
17
|
+
queue?: Queue,
|
|
15
18
|
): SpaceGraphModel | undefined => {
|
|
19
|
+
const registry = useCapability(Capabilities.AtomRegistry);
|
|
16
20
|
const [model, setModel] = useState<SpaceGraphModel | undefined>(undefined);
|
|
17
21
|
useEffect(() => {
|
|
18
22
|
if (!space) {
|
|
@@ -22,14 +26,14 @@ export const useGraphModel = (
|
|
|
22
26
|
}
|
|
23
27
|
|
|
24
28
|
// TODO(burdon): Does this need to be a dependency?
|
|
25
|
-
if (!model) {
|
|
26
|
-
const model = new SpaceGraphModel().setFilter(filter).setOptions(options);
|
|
27
|
-
void model.open(space);
|
|
29
|
+
if (!model || model.queue !== queue) {
|
|
30
|
+
const model = new SpaceGraphModel(registry).setFilter(filter).setOptions(options);
|
|
31
|
+
void model.open(space.db, queue);
|
|
28
32
|
setModel(model);
|
|
29
33
|
} else {
|
|
30
34
|
model.setFilter(filter).setOptions(options);
|
|
31
35
|
}
|
|
32
|
-
}, [space, filter, options]);
|
|
36
|
+
}, [space, filter, options, queue, registry]);
|
|
33
37
|
|
|
34
38
|
return model;
|
|
35
39
|
};
|
package/src/meta.ts
CHANGED
|
@@ -2,15 +2,18 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { type
|
|
5
|
+
import { type Plugin } from '@dxos/app-framework';
|
|
6
|
+
import { trim } from '@dxos/util';
|
|
6
7
|
|
|
7
|
-
export const
|
|
8
|
-
|
|
9
|
-
export const meta: PluginMeta = {
|
|
10
|
-
id: EXPLORER_PLUGIN,
|
|
8
|
+
export const meta: Plugin.Meta = {
|
|
9
|
+
id: 'dxos.org/plugin/explorer',
|
|
11
10
|
name: 'Explorer',
|
|
12
|
-
description:
|
|
11
|
+
description: trim`
|
|
12
|
+
Interactive hypergraph visualization that reveals relationships between objects in your workspace.
|
|
13
|
+
Navigate complex data structures and discover connections through a dynamic network view.
|
|
14
|
+
`,
|
|
13
15
|
icon: 'ph--graph--regular',
|
|
16
|
+
iconHue: 'green',
|
|
14
17
|
source: 'https://github.com/dxos/dxos/tree/main/packages/plugins/plugin-explorer',
|
|
15
18
|
tags: ['labs'],
|
|
16
19
|
screenshots: ['https://dxos.network/plugin-details-explorer-dark.png'],
|