@ts-graphviz/react 0.9.1 → 0.10.0
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/CHANGELOG.md +76 -0
- package/LICENSE +1 -1
- package/README.md +25 -63
- package/example/example.tsx +14 -12
- package/lib/react.cjs +457 -0
- package/lib/react.d.ts +230 -0
- package/lib/react.js +457 -0
- package/package.json +55 -65
- package/src/components/Digraph.spec.tsx +18 -0
- package/src/components/Digraph.tsx +41 -0
- package/src/components/Edge.tsx +17 -0
- package/src/components/Graph.spec.tsx +18 -0
- package/src/components/Graph.tsx +36 -0
- package/src/components/GraphPortal.tsx +22 -0
- package/src/components/Node.spec.tsx +101 -0
- package/src/components/Node.tsx +21 -0
- package/src/components/Subgraph.tsx +30 -0
- package/src/contexts/CurrentGraph.ts +5 -0
- package/src/contexts/GraphContainer.ts +5 -0
- package/src/contexts/GraphMap.ts +5 -0
- package/src/contexts/GraphvizContext.ts +11 -0
- package/src/hooks/__tests__/wrapper.tsx +58 -0
- package/src/hooks/useCurrentGraph.spec.ts +56 -0
- package/src/hooks/useCurrentGraph.ts +18 -0
- package/src/hooks/useDigraph.spec.ts +15 -0
- package/src/hooks/useDigraph.ts +33 -0
- package/src/hooks/useEdge.spec.ts +46 -0
- package/src/hooks/useEdge.ts +35 -0
- package/src/hooks/useGraph.spec.ts +15 -0
- package/src/hooks/useGraph.ts +33 -0
- package/src/hooks/useGraphAttributes.ts +32 -0
- package/src/hooks/useGraphContainer.spec.ts +45 -0
- package/src/hooks/useGraphContainer.ts +10 -0
- package/src/hooks/useGraphMap.ts +7 -0
- package/src/hooks/useGraphvizContext.spec.ts +26 -0
- package/src/hooks/useGraphvizContext.ts +16 -0
- package/src/hooks/useHasAttributes.ts +16 -0
- package/src/hooks/useHasComment.ts +8 -0
- package/src/hooks/useNode.spec.ts +16 -0
- package/src/hooks/useNode.ts +29 -0
- package/src/hooks/useRenderedID.spec.tsx +28 -0
- package/src/hooks/useRenderedID.ts +6 -0
- package/src/hooks/useSubgraph.spec.ts +41 -0
- package/src/hooks/useSubgraph.ts +39 -0
- package/src/react.ts +16 -0
- package/src/reconciler.ts +272 -0
- package/src/render.test.tsx +71 -0
- package/src/render.ts +142 -0
- package/src/renderHTMLLike.ts +9 -0
- package/src/renderId.ts +9 -0
- package/src/types.ts +107 -0
- package/tsconfig.json +8 -0
- package/typedoc.json +5 -0
- package/vite.config.ts +31 -0
- package/.all-contributorsrc +0 -47
- package/.devcontainer/Dockerfile +0 -16
- package/.devcontainer/devcontainer.json +0 -35
- package/.eslintignore +0 -2
- package/lib/index.d.ts +0 -368
- package/lib/index.js +0 -1
- package/lib/index.mjs +0 -1
- package/rollup.config.js +0 -55
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
import { renderHook } from '@testing-library/react';
|
|
3
|
+
import { describe, expect, test } from 'vitest';
|
|
4
|
+
import { context } from './__tests__/wrapper.js';
|
|
5
|
+
import { useGraphvizContext } from './useGraphvizContext.js';
|
|
6
|
+
|
|
7
|
+
describe('useGraphvizContext', () => {
|
|
8
|
+
test('returns {}', () => {
|
|
9
|
+
const { result } = renderHook(() => useGraphvizContext(), {
|
|
10
|
+
wrapper: context(),
|
|
11
|
+
});
|
|
12
|
+
expect(result.current).toStrictEqual({});
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test('An error occurs when called outside the GraphvizContext', () => {
|
|
16
|
+
expect(() => {
|
|
17
|
+
renderHook(() => useGraphvizContext(), {
|
|
18
|
+
// Don't use context() here
|
|
19
|
+
// wrapper: context(),
|
|
20
|
+
});
|
|
21
|
+
}).toThrowErrorMatchingInlineSnapshot(`
|
|
22
|
+
[Error: Cannot call useGraphvizContext outside GraphvizContext.
|
|
23
|
+
Basically, you need to use the render function provided by @ts-graphviz/react.]
|
|
24
|
+
`);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import { useContext } from 'react';
|
|
3
|
+
import type { GraphBaseModel } from 'ts-graphviz';
|
|
4
|
+
import { type Context, GraphvizContext } from '../contexts/GraphvizContext.js';
|
|
5
|
+
|
|
6
|
+
export function useGraphvizContext<T extends GraphBaseModel>(): Context<T> {
|
|
7
|
+
const context = useContext(
|
|
8
|
+
GraphvizContext as unknown as React.Context<Context<T>>,
|
|
9
|
+
);
|
|
10
|
+
if (context === null) {
|
|
11
|
+
throw Error(
|
|
12
|
+
'Cannot call useGraphvizContext outside GraphvizContext.\nBasically, you need to use the render function provided by @ts-graphviz/react.',
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
return context;
|
|
16
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import type {
|
|
3
|
+
AttributeKey,
|
|
4
|
+
AttributesObject,
|
|
5
|
+
HasAttributes,
|
|
6
|
+
} from 'ts-graphviz';
|
|
7
|
+
|
|
8
|
+
export function useHasAttributes<T extends AttributeKey>(
|
|
9
|
+
target: HasAttributes<T>,
|
|
10
|
+
attributes: AttributesObject<T>,
|
|
11
|
+
): void {
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
target.attributes.clear();
|
|
14
|
+
target.attributes.apply(attributes);
|
|
15
|
+
}, [target, attributes]);
|
|
16
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
import { renderHook } from '@testing-library/react';
|
|
3
|
+
import { Node } from 'ts-graphviz';
|
|
4
|
+
import { describe, expect, it } from 'vitest';
|
|
5
|
+
|
|
6
|
+
import { digraph } from './__tests__/wrapper.js';
|
|
7
|
+
import { useNode } from './useNode.js';
|
|
8
|
+
|
|
9
|
+
describe('useNode', () => {
|
|
10
|
+
it('returns Node instance', () => {
|
|
11
|
+
const { result } = renderHook(() => useNode('hoge'), {
|
|
12
|
+
wrapper: digraph(),
|
|
13
|
+
});
|
|
14
|
+
expect(result.current).toBeInstanceOf(Node);
|
|
15
|
+
});
|
|
16
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { useEffect, useMemo } from 'react';
|
|
2
|
+
import type { NodeModel } from 'ts-graphviz';
|
|
3
|
+
import type { NodeOptions } from '../types.js';
|
|
4
|
+
import { useCurrentGraph } from './useCurrentGraph.js';
|
|
5
|
+
import { useHasAttributes } from './useHasAttributes.js';
|
|
6
|
+
import { useHasComment } from './useHasComment.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* `useNode` is a hook that creates an instance of Node
|
|
10
|
+
* according to the object given by props.
|
|
11
|
+
*/
|
|
12
|
+
export function useNode(id: string, options: NodeOptions = {}): NodeModel {
|
|
13
|
+
const { comment, ...attributes } = options;
|
|
14
|
+
const cluster = useCurrentGraph();
|
|
15
|
+
const node = useMemo(() => {
|
|
16
|
+
const n = cluster.createNode(id);
|
|
17
|
+
n.attributes.apply(attributes);
|
|
18
|
+
n.comment = comment;
|
|
19
|
+
return n;
|
|
20
|
+
}, [cluster, id, attributes, comment]);
|
|
21
|
+
useHasComment(node, comment);
|
|
22
|
+
useHasAttributes(node, attributes);
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
return (): void => {
|
|
25
|
+
cluster.removeNode(node);
|
|
26
|
+
};
|
|
27
|
+
}, [cluster, node]);
|
|
28
|
+
return node;
|
|
29
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
import { renderHook } from '@testing-library/react';
|
|
3
|
+
import { describe, expect, it } from 'vitest';
|
|
4
|
+
import { context } from './__tests__/wrapper.js';
|
|
5
|
+
import { useRenderedID } from './useRenderedID.js';
|
|
6
|
+
|
|
7
|
+
describe('useRenderedID', () => {
|
|
8
|
+
it('should return the same value as the input, when input is string', () => {
|
|
9
|
+
const { result } = renderHook(() => useRenderedID('foo'), {
|
|
10
|
+
wrapper: context(),
|
|
11
|
+
});
|
|
12
|
+
expect(result.current).toBe('foo');
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('should return the same value as the input, when input is HTMLLike string', () => {
|
|
16
|
+
const { result } = renderHook(() => useRenderedID('<<b>bold</b>>'), {
|
|
17
|
+
wrapper: context(),
|
|
18
|
+
});
|
|
19
|
+
expect(result.current).toBe('<<b>bold</b>>');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('should return the HTMLLike string, when input is JSX', () => {
|
|
23
|
+
const { result } = renderHook(() => useRenderedID(<dot:b>bold</dot:b>), {
|
|
24
|
+
wrapper: context(),
|
|
25
|
+
});
|
|
26
|
+
expect(result.current).toBe('<<b>bold</b>>');
|
|
27
|
+
});
|
|
28
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
import { renderHook } from '@testing-library/react';
|
|
3
|
+
import { Subgraph } from 'ts-graphviz';
|
|
4
|
+
import { describe, expect, it } from 'vitest';
|
|
5
|
+
import {
|
|
6
|
+
digraph,
|
|
7
|
+
digraphInSubgraph,
|
|
8
|
+
graph,
|
|
9
|
+
graphInSubgraph,
|
|
10
|
+
} from './__tests__/wrapper.js';
|
|
11
|
+
import { useSubgraph } from './useSubgraph.js';
|
|
12
|
+
|
|
13
|
+
describe('useSubgraph', () => {
|
|
14
|
+
it('returns Subgraph instance in digraph warper', () => {
|
|
15
|
+
const { result } = renderHook(() => useSubgraph(), {
|
|
16
|
+
wrapper: digraph(),
|
|
17
|
+
});
|
|
18
|
+
expect(result.current).toBeInstanceOf(Subgraph);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('returns Subgraph instance in graph warper', () => {
|
|
22
|
+
const { result } = renderHook(() => useSubgraph(), {
|
|
23
|
+
wrapper: graph(),
|
|
24
|
+
});
|
|
25
|
+
expect(result.current).toBeInstanceOf(Subgraph);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('returns Subgraph instance in graphInSubgraph warper', () => {
|
|
29
|
+
const { result } = renderHook(() => useSubgraph(), {
|
|
30
|
+
wrapper: graphInSubgraph(),
|
|
31
|
+
});
|
|
32
|
+
expect(result.current).toBeInstanceOf(Subgraph);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('returns Subgraph instance in digraphInSubgraph warper', () => {
|
|
36
|
+
const { result } = renderHook(() => useSubgraph(), {
|
|
37
|
+
wrapper: digraphInSubgraph(),
|
|
38
|
+
});
|
|
39
|
+
expect(result.current).toBeInstanceOf(Subgraph);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { useEffect, useMemo } from 'react';
|
|
2
|
+
import { type GraphBaseModel, Subgraph, type SubgraphModel } from 'ts-graphviz';
|
|
3
|
+
import type { SubgraphOptions } from '../types.js';
|
|
4
|
+
import { useCurrentGraph } from './useCurrentGraph.js';
|
|
5
|
+
import { useGraphAttributes } from './useGraphAttributes.js';
|
|
6
|
+
import { useGraphvizContext } from './useGraphvizContext.js';
|
|
7
|
+
import { useHasComment } from './useHasComment.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* `useSubgraph` is a hook that creates an instance of Subgraph
|
|
11
|
+
* according to the object given by props.
|
|
12
|
+
*/
|
|
13
|
+
export function useSubgraph(props: SubgraphOptions = {}): SubgraphModel {
|
|
14
|
+
const { id, comment, edge, node, graph, ...attributes } = props;
|
|
15
|
+
const context = useGraphvizContext();
|
|
16
|
+
const cluster = useCurrentGraph();
|
|
17
|
+
const subgraph = useMemo(() => {
|
|
18
|
+
const g = new Subgraph(id);
|
|
19
|
+
if (cluster !== null) {
|
|
20
|
+
cluster.addSubgraph(g);
|
|
21
|
+
} else if (!context.container) {
|
|
22
|
+
context.container = g as GraphBaseModel;
|
|
23
|
+
}
|
|
24
|
+
g.comment = comment;
|
|
25
|
+
g.apply(attributes);
|
|
26
|
+
g.attributes.node.apply(node ?? {});
|
|
27
|
+
g.attributes.edge.apply(edge ?? {});
|
|
28
|
+
g.attributes.graph.apply(graph ?? {});
|
|
29
|
+
return g;
|
|
30
|
+
}, [context, cluster, id, comment, edge, node, graph, attributes]);
|
|
31
|
+
useHasComment(subgraph, comment);
|
|
32
|
+
useGraphAttributes(subgraph, attributes, { edge, node, graph });
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
return (): void => {
|
|
35
|
+
cluster.removeSubgraph(subgraph);
|
|
36
|
+
};
|
|
37
|
+
}, [cluster, subgraph]);
|
|
38
|
+
return subgraph;
|
|
39
|
+
}
|
package/src/react.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export * from './types.js';
|
|
2
|
+
export * from './hooks/useGraphContainer.js';
|
|
3
|
+
export * from './hooks/useCurrentGraph.js';
|
|
4
|
+
export * from './hooks/useDigraph.js';
|
|
5
|
+
export * from './hooks/useGraph.js';
|
|
6
|
+
export * from './hooks/useSubgraph.js';
|
|
7
|
+
export * from './hooks/useEdge.js';
|
|
8
|
+
export * from './hooks/useNode.js';
|
|
9
|
+
export * from './components/Graph.js';
|
|
10
|
+
export * from './components/Digraph.js';
|
|
11
|
+
export * from './components/Subgraph.js';
|
|
12
|
+
export * from './components/Node.js';
|
|
13
|
+
export * from './components/Edge.js';
|
|
14
|
+
export * from './components/GraphPortal.js';
|
|
15
|
+
export * from './renderHTMLLike.js';
|
|
16
|
+
export * from './render.js';
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import type { FC } from 'react';
|
|
2
|
+
import ReactReconciler from 'react-reconciler';
|
|
3
|
+
|
|
4
|
+
type Type = FC;
|
|
5
|
+
type Props = any;
|
|
6
|
+
type Container = Record<string, never>;
|
|
7
|
+
|
|
8
|
+
type Instance = any;
|
|
9
|
+
type TextInstance = any;
|
|
10
|
+
type SuspenseInstance = any;
|
|
11
|
+
type HydratableInstance = any;
|
|
12
|
+
type PublicInstance = any;
|
|
13
|
+
type HostContext = any;
|
|
14
|
+
type UpdatePayload = any;
|
|
15
|
+
type ChildSet = any;
|
|
16
|
+
type TimeoutHandle = any;
|
|
17
|
+
type NoTimeout = any;
|
|
18
|
+
|
|
19
|
+
type OpaqueHandle = ReactReconciler.Fiber;
|
|
20
|
+
|
|
21
|
+
export class HostConfig
|
|
22
|
+
implements
|
|
23
|
+
ReactReconciler.HostConfig<
|
|
24
|
+
Type,
|
|
25
|
+
Props,
|
|
26
|
+
Container,
|
|
27
|
+
Instance,
|
|
28
|
+
TextInstance,
|
|
29
|
+
SuspenseInstance,
|
|
30
|
+
HydratableInstance,
|
|
31
|
+
PublicInstance,
|
|
32
|
+
HostContext,
|
|
33
|
+
UpdatePayload,
|
|
34
|
+
ChildSet, // TODO Placeholder for undocumented API
|
|
35
|
+
TimeoutHandle,
|
|
36
|
+
NoTimeout
|
|
37
|
+
>
|
|
38
|
+
{
|
|
39
|
+
preparePortalMount(containerInfo: Container): void {
|
|
40
|
+
// NoOp
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
scheduleTimeout(fn: (...args: unknown[]) => unknown, delay?: number) {
|
|
44
|
+
// NoOp
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
cancelTimeout(id: any): void {
|
|
48
|
+
// NoOp
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
queueMicrotask(fn: () => void): void {
|
|
52
|
+
// NoOp
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
cloneInstance?: any;
|
|
56
|
+
|
|
57
|
+
cloneFundamentalInstance?: any;
|
|
58
|
+
|
|
59
|
+
createContainerChildSet?: any;
|
|
60
|
+
|
|
61
|
+
appendChildToContainerChildSet?: any;
|
|
62
|
+
|
|
63
|
+
finalizeContainerChildren?: any;
|
|
64
|
+
|
|
65
|
+
replaceContainerChildren?: any;
|
|
66
|
+
|
|
67
|
+
cloneHiddenInstance?: any;
|
|
68
|
+
|
|
69
|
+
cloneHiddenTextInstance?: any;
|
|
70
|
+
|
|
71
|
+
public now = Date.now;
|
|
72
|
+
|
|
73
|
+
public setTimeout = setTimeout;
|
|
74
|
+
|
|
75
|
+
public clearTimeout = clearTimeout;
|
|
76
|
+
|
|
77
|
+
public noTimeout: NoTimeout = -1; // TODO
|
|
78
|
+
|
|
79
|
+
// Temporary workaround for scenario where multiple renderers concurrently
|
|
80
|
+
// render using the same context objects. E.g. React DOM and React ART on the
|
|
81
|
+
// same page. DOM is the primary renderer; ART is the secondary renderer.
|
|
82
|
+
public isPrimaryRenderer = false;
|
|
83
|
+
|
|
84
|
+
public supportsMutation = false;
|
|
85
|
+
|
|
86
|
+
public supportsPersistence = false;
|
|
87
|
+
|
|
88
|
+
public supportsHydration = false;
|
|
89
|
+
|
|
90
|
+
public getPublicInstance(instance: Instance | TextInstance): PublicInstance {
|
|
91
|
+
return instance;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
public getRootHostContext(rootContainerInstance: Container): HostContext {
|
|
95
|
+
return {};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
public getChildHostContext(
|
|
99
|
+
parentHostContext: HostContext,
|
|
100
|
+
type: Type,
|
|
101
|
+
rootContainerInstance: Container,
|
|
102
|
+
): HostContext {
|
|
103
|
+
return parentHostContext;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
public prepareForCommit(
|
|
107
|
+
containerInfo: Container,
|
|
108
|
+
): Record<string, any> | null {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
public resetAfterCommit(containerInfo: Container): void {
|
|
113
|
+
// containerInfo.setRoot
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Create component instance
|
|
118
|
+
*/
|
|
119
|
+
public createInstance(
|
|
120
|
+
type: Type,
|
|
121
|
+
props: Props,
|
|
122
|
+
rootContainerInstance: Container,
|
|
123
|
+
hostContext: HostContext,
|
|
124
|
+
internalInstanceHandle: OpaqueHandle,
|
|
125
|
+
): Instance {
|
|
126
|
+
// NoOp
|
|
127
|
+
return type(props);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
public appendInitialChild(
|
|
131
|
+
parentInstance: Instance,
|
|
132
|
+
child: Instance | TextInstance,
|
|
133
|
+
): void {
|
|
134
|
+
parentInstance.appendChild(child);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
public finalizeInitialChildren(
|
|
138
|
+
parentInstance: Instance,
|
|
139
|
+
type: Type,
|
|
140
|
+
props: Props,
|
|
141
|
+
rootContainerInstance: Container,
|
|
142
|
+
hostContext: HostContext,
|
|
143
|
+
): boolean {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
public prepareUpdate(
|
|
148
|
+
instance: Instance,
|
|
149
|
+
type: Type,
|
|
150
|
+
oldProps: Props,
|
|
151
|
+
newProps: Props,
|
|
152
|
+
rootContainerInstance: Container,
|
|
153
|
+
hostContext: HostContext,
|
|
154
|
+
): null | UpdatePayload {
|
|
155
|
+
return {};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
public shouldSetTextContent(type: Type, props: Props): boolean {
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
public shouldDeprioritizeSubtree(type: Type, props: Props): boolean {
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
public createTextInstance(
|
|
167
|
+
text: string,
|
|
168
|
+
rootContainerInstance: Container,
|
|
169
|
+
hostContext: HostContext,
|
|
170
|
+
internalInstanceHandle: OpaqueHandle,
|
|
171
|
+
): TextInstance {
|
|
172
|
+
return text;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
public scheduleDeferredCallback(
|
|
176
|
+
callback: () => any,
|
|
177
|
+
options?: { timeout: number },
|
|
178
|
+
): any {
|
|
179
|
+
// NoOp
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
public cancelDeferredCallback(callbackID: any): void {
|
|
183
|
+
// NoOp
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// -------------------
|
|
187
|
+
// Mutation
|
|
188
|
+
// (optional)
|
|
189
|
+
// -------------------
|
|
190
|
+
public appendChild(
|
|
191
|
+
parentInstance: Instance,
|
|
192
|
+
child: Instance | TextInstance,
|
|
193
|
+
): void {
|
|
194
|
+
// NoOp
|
|
195
|
+
if (parentInstance.appendChild) {
|
|
196
|
+
parentInstance.appendChild(child);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
public appendChildToContainer(
|
|
201
|
+
container: Container,
|
|
202
|
+
child: Instance | TextInstance,
|
|
203
|
+
): void {
|
|
204
|
+
// if (container.appendChild) {
|
|
205
|
+
// container.appendChild(child);
|
|
206
|
+
// }
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
public commitTextUpdate(
|
|
210
|
+
textInstance: TextInstance,
|
|
211
|
+
oldText: string,
|
|
212
|
+
newText: string,
|
|
213
|
+
): void {
|
|
214
|
+
// NoOp
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
public commitMount(
|
|
218
|
+
instance: Instance,
|
|
219
|
+
type: Type,
|
|
220
|
+
newProps: Props,
|
|
221
|
+
internalInstanceHandle: OpaqueHandle,
|
|
222
|
+
): void {
|
|
223
|
+
// NoOp
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
public commitUpdate(
|
|
227
|
+
instance: Instance,
|
|
228
|
+
updatePayload: UpdatePayload,
|
|
229
|
+
type: Type,
|
|
230
|
+
oldProps: Props,
|
|
231
|
+
newProps: Props,
|
|
232
|
+
internalInstanceHandle: OpaqueHandle,
|
|
233
|
+
): void {
|
|
234
|
+
// NoOp
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
public insertBefore(
|
|
238
|
+
parentInstance: Instance,
|
|
239
|
+
child: Instance | TextInstance,
|
|
240
|
+
beforeChild: Instance | TextInstance,
|
|
241
|
+
): void {
|
|
242
|
+
// NoOp
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
public insertInContainerBefore(
|
|
246
|
+
container: Container,
|
|
247
|
+
child: Instance | TextInstance,
|
|
248
|
+
beforeChild: Instance | TextInstance,
|
|
249
|
+
): void {
|
|
250
|
+
// NoOp
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
public removeChild(
|
|
254
|
+
parentInstance: Instance,
|
|
255
|
+
child: Instance | TextInstance,
|
|
256
|
+
): void {
|
|
257
|
+
// NoOp
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
public removeChildFromContainer(
|
|
261
|
+
container: Container,
|
|
262
|
+
child: Instance | TextInstance,
|
|
263
|
+
): void {
|
|
264
|
+
// NoOp
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
public resetTextContent(instance: Instance): void {
|
|
268
|
+
// NoOp
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
export const reconciler = ReactReconciler(new HostConfig());
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { Digraph } from './components/Digraph.js';
|
|
3
|
+
import { Edge } from './components/Edge.js';
|
|
4
|
+
import { Graph } from './components/Graph.js';
|
|
5
|
+
import { Node } from './components/Node.js';
|
|
6
|
+
import { Subgraph } from './components/Subgraph.js';
|
|
7
|
+
import { renderToDot } from './render.js';
|
|
8
|
+
|
|
9
|
+
describe('renderToDot', () => {
|
|
10
|
+
it('should render a digraph', () => {
|
|
11
|
+
expect(
|
|
12
|
+
renderToDot(
|
|
13
|
+
<Digraph>
|
|
14
|
+
<Node id="a" label="Node A" />
|
|
15
|
+
<Node id="b" label={<dot:b>bold</dot:b>} />
|
|
16
|
+
<Subgraph id="cluster_x" label="Cluster X">
|
|
17
|
+
<Node id="c" label="Node C" />
|
|
18
|
+
</Subgraph>
|
|
19
|
+
<Edge targets={['a', 'b']} />
|
|
20
|
+
</Digraph>,
|
|
21
|
+
),
|
|
22
|
+
).toMatchInlineSnapshot(`
|
|
23
|
+
"digraph {
|
|
24
|
+
"a" [
|
|
25
|
+
label = "Node A";
|
|
26
|
+
];
|
|
27
|
+
"b" [
|
|
28
|
+
label = <<b>bold</b>>;
|
|
29
|
+
];
|
|
30
|
+
subgraph "cluster_x" {
|
|
31
|
+
label = "Cluster X";
|
|
32
|
+
"c" [
|
|
33
|
+
label = "Node C";
|
|
34
|
+
];
|
|
35
|
+
}
|
|
36
|
+
"a" -> "b";
|
|
37
|
+
}"
|
|
38
|
+
`);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should render a graph', () => {
|
|
42
|
+
expect(
|
|
43
|
+
renderToDot(
|
|
44
|
+
<Graph>
|
|
45
|
+
<Node id="a" label="Node A" />
|
|
46
|
+
<Node id="b" label="Node B" />
|
|
47
|
+
<Subgraph id="cluster_x" label="Cluster X">
|
|
48
|
+
<Node id="c" label="Node C" />
|
|
49
|
+
</Subgraph>
|
|
50
|
+
<Edge targets={['a', 'b']} />
|
|
51
|
+
</Graph>,
|
|
52
|
+
),
|
|
53
|
+
).toMatchInlineSnapshot(`
|
|
54
|
+
"graph {
|
|
55
|
+
"a" [
|
|
56
|
+
label = "Node A";
|
|
57
|
+
];
|
|
58
|
+
"b" [
|
|
59
|
+
label = "Node B";
|
|
60
|
+
];
|
|
61
|
+
subgraph "cluster_x" {
|
|
62
|
+
label = "Cluster X";
|
|
63
|
+
"c" [
|
|
64
|
+
label = "Node C";
|
|
65
|
+
];
|
|
66
|
+
}
|
|
67
|
+
"a" -- "b";
|
|
68
|
+
}"
|
|
69
|
+
`);
|
|
70
|
+
});
|
|
71
|
+
});
|