@dxos/app-graph 0.3.3-main.052ad73

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 (40) hide show
  1. package/LICENSE +8 -0
  2. package/README.md +13 -0
  3. package/dist/lib/browser/index.mjs +252 -0
  4. package/dist/lib/browser/index.mjs.map +7 -0
  5. package/dist/lib/browser/meta.json +1 -0
  6. package/dist/lib/browser/testing.mjs +98 -0
  7. package/dist/lib/browser/testing.mjs.map +7 -0
  8. package/dist/lib/node/index.cjs +290 -0
  9. package/dist/lib/node/index.cjs.map +7 -0
  10. package/dist/lib/node/meta.json +1 -0
  11. package/dist/lib/node/testing.cjs +124 -0
  12. package/dist/lib/node/testing.cjs.map +7 -0
  13. package/dist/types/src/action.d.ts +53 -0
  14. package/dist/types/src/action.d.ts.map +1 -0
  15. package/dist/types/src/graph-builder.d.ts +31 -0
  16. package/dist/types/src/graph-builder.d.ts.map +1 -0
  17. package/dist/types/src/graph.d.ts +45 -0
  18. package/dist/types/src/graph.d.ts.map +1 -0
  19. package/dist/types/src/graph.test.d.ts +2 -0
  20. package/dist/types/src/graph.test.d.ts.map +1 -0
  21. package/dist/types/src/index.d.ts +5 -0
  22. package/dist/types/src/index.d.ts.map +1 -0
  23. package/dist/types/src/node.d.ts +71 -0
  24. package/dist/types/src/node.d.ts.map +1 -0
  25. package/dist/types/src/stories/EchoGraph.stories.d.ts +10 -0
  26. package/dist/types/src/stories/EchoGraph.stories.d.ts.map +1 -0
  27. package/dist/types/src/stories/Tree.d.ts +14 -0
  28. package/dist/types/src/stories/Tree.d.ts.map +1 -0
  29. package/dist/types/src/testing.d.ts +52 -0
  30. package/dist/types/src/testing.d.ts.map +1 -0
  31. package/package.json +74 -0
  32. package/src/action.ts +69 -0
  33. package/src/graph-builder.ts +200 -0
  34. package/src/graph.test.ts +220 -0
  35. package/src/graph.ts +110 -0
  36. package/src/index.ts +8 -0
  37. package/src/node.ts +101 -0
  38. package/src/stories/EchoGraph.stories.tsx +233 -0
  39. package/src/stories/Tree.tsx +79 -0
  40. package/src/testing.ts +152 -0
@@ -0,0 +1,79 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import React, { type FC, type HTMLAttributes, useState } from 'react';
6
+
7
+ import { mx } from '@dxos/react-ui-theme';
8
+
9
+ // TODO(burdon): Copied form devtools.
10
+
11
+ export const Tree: FC<{ data?: object }> = ({ data }) => {
12
+ return (
13
+ <div className='flex overflow-auto ml-2 border-l-2 border-blue-500'>
14
+ <Node data={data} root />
15
+ </div>
16
+ );
17
+ };
18
+
19
+ export const Node: FC<{ data?: any; root?: boolean }> = ({ data, root }) => {
20
+ if (typeof data !== 'object' || data === undefined || data === null) {
21
+ return <Scalar value={data} />;
22
+ }
23
+
24
+ if (Array.isArray(data)) {
25
+ return (
26
+ <div className='flex flex-col space-y-2'>
27
+ {data.map((value, index) => (
28
+ <KeyValue key={index} label={String(index)} data={value} className='bg-teal-50' />
29
+ ))}
30
+ </div>
31
+ );
32
+ }
33
+
34
+ return (
35
+ <div className='flex flex-col space-y-2'>
36
+ {Object.entries(data).map(([key, value]) => (
37
+ <KeyValue key={key} label={key} data={value} className='bg-blue-50' />
38
+ ))}
39
+ </div>
40
+ );
41
+ };
42
+
43
+ export const KeyValue: FC<{ label: string; data?: any; className?: string }> = ({ label, data, className }) => {
44
+ const [open, setOpen] = useState(true);
45
+ if (data === undefined) {
46
+ return null;
47
+ }
48
+
49
+ return (
50
+ <div className='flex'>
51
+ <Box
52
+ className={mx('border-blue-200 text-sm select-none cursor-pointer', className)}
53
+ onClick={() => setOpen((open) => !open)}
54
+ >
55
+ {label}
56
+ </Box>
57
+ {open && <Node data={data} />}
58
+ </div>
59
+ );
60
+ };
61
+
62
+ const Scalar: FC<{ value: any }> = ({ value }) => {
63
+ return (
64
+ <Box className='bg-green-50 border-green-200 rounded-r text-sm font-thin'>
65
+ {(value === undefined && 'undefined') ||
66
+ (value === null && 'null') ||
67
+ (typeof value === 'string' && value) ||
68
+ JSON.stringify(value)}
69
+ </Box>
70
+ );
71
+ };
72
+
73
+ const Box: FC<HTMLAttributes<HTMLDivElement>> = ({ children, className, ...props }) => {
74
+ return (
75
+ <div className={mx('flex px-2 border border-l-0 font-mono truncate', className)} {...props}>
76
+ {children}
77
+ </div>
78
+ );
79
+ };
package/src/testing.ts ADDED
@@ -0,0 +1,152 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import { type Action } from './action';
6
+ import { type Graph } from './graph';
7
+ import { type NodeBuilder, type Node } from './node';
8
+
9
+ /**
10
+ * Create a test node builder that always adds nodes and actions to the specified depth.
11
+ *
12
+ * @param id The id of the node builder, used to identify nodes created by this builder.
13
+ * @param depth The depth at which to add nodes and actions.
14
+ * @default 1
15
+ *
16
+ * @returns A test node builder
17
+ */
18
+ // TODO(burdon): Change to TestNodeBuilder class (see other builder/generator patterns in client/echo).
19
+ export const createTestNodeBuilder = (id: string, depth = 1) => {
20
+ const nodes = new Map<string, Node>();
21
+ const nodeBuilder: NodeBuilder = (parent) => {
22
+ if (checkDepth(parent) >= depth) {
23
+ return;
24
+ }
25
+
26
+ const [child] = parent.addNode(id, {
27
+ id: `${parent.id}-${id}`,
28
+ label: `${parent.id}-${id}`,
29
+ data: null,
30
+ parent,
31
+ });
32
+
33
+ parent.addAction({
34
+ id: `${parent.id}-${id}`,
35
+ label: `${parent.id}-${id}`,
36
+ invoke: () => {},
37
+ });
38
+
39
+ nodes.set(parent.id, parent);
40
+ nodes.set(child.id, child);
41
+ };
42
+
43
+ const addNode = (parentId: string, node: Pick<Node, 'id' | 'label'> & Partial<Node>) => {
44
+ const parent = nodes.get(parentId);
45
+ if (!parent) {
46
+ return;
47
+ }
48
+
49
+ const [child] = parent.addNode(id, node);
50
+ nodes.set(child.id, child);
51
+ return child;
52
+ };
53
+
54
+ const removeNode = (parentId: string, id: string) => {
55
+ const parent = nodes.get(parentId);
56
+ if (!parent) {
57
+ return;
58
+ }
59
+
60
+ return parent.removeNode(id);
61
+ };
62
+
63
+ const addAction = (parentId: string, action: Pick<Action, 'id' | 'label' | 'invoke'> & Partial<Action>) => {
64
+ const parent = nodes.get(parentId);
65
+ if (!parent) {
66
+ return;
67
+ }
68
+
69
+ return parent.addAction(action);
70
+ };
71
+
72
+ const removeAction = (parentId: string, id: string) => {
73
+ const parent = nodes.get(parentId);
74
+ if (!parent) {
75
+ return;
76
+ }
77
+
78
+ return parent.removeAction(id);
79
+ };
80
+
81
+ const addProperty = (parentId: string, key: string, value: any) => {
82
+ const parent = nodes.get(parentId);
83
+ if (!parent) {
84
+ return;
85
+ }
86
+
87
+ return parent.addProperty(key, value);
88
+ };
89
+
90
+ const removeProperty = (parentId: string, key: string) => {
91
+ const parent = nodes.get(parentId);
92
+ if (!parent) {
93
+ return;
94
+ }
95
+
96
+ return parent.removeProperty(key);
97
+ };
98
+
99
+ return { nodeBuilder, addNode, removeNode, addAction, removeAction, addProperty, removeProperty };
100
+ };
101
+
102
+ /**
103
+ * Build a graph from a nested list of nodes.
104
+ *
105
+ * @param graph Graph to add nodes to.
106
+ * @param nodes Nodes to add to the
107
+ *
108
+ * @example
109
+ * const graph = new GraphStore();
110
+ * buildGraph(graph, [
111
+ * {
112
+ * id: 'test1',
113
+ * label: 'test1',
114
+ * children: [
115
+ * {
116
+ * id: 'test1.1',
117
+ * label: 'test1.1',
118
+ * },
119
+ * {
120
+ * id: 'test1.2',
121
+ * label: 'test1.2',
122
+ * },
123
+ * ],
124
+ * },
125
+ * {
126
+ * id: 'test2',
127
+ * label: 'test2',
128
+ * },
129
+ * ]);
130
+ */
131
+
132
+ // TODO(wittjosiah): Type nodes.
133
+ export const buildGraph = (graph: Graph, id: string, nodes: any[]) => {
134
+ addNodes(graph.root, id, nodes);
135
+ return graph;
136
+ };
137
+
138
+ const addNodes = (root: Node, id: string, nodes: any[]) => {
139
+ nodes.forEach((node) => {
140
+ const [child] = root.addNode(id, node);
141
+ addNodes(child, id, node.children || []);
142
+ node.actions?.forEach((action: any) => child.addAction(action));
143
+ });
144
+ };
145
+
146
+ const checkDepth = (node: Node, depth = 0): number => {
147
+ if (!node.parent) {
148
+ return depth;
149
+ }
150
+
151
+ return checkDepth(node.parent, depth + 1);
152
+ };