@ts-graphviz/react 0.10.6 → 0.11.1
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 +497 -0
- package/README.md +630 -67
- package/example/example.tsx +1 -1
- package/lib/react.d.ts +198 -96
- package/lib/react.js +558 -215
- package/package.json +15 -20
- package/tsconfig.test.json +11 -0
- package/lib/react.cjs +0 -457
- package/src/components/Digraph.spec.tsx +0 -18
- package/src/components/Digraph.tsx +0 -41
- package/src/components/Edge.tsx +0 -17
- package/src/components/Graph.spec.tsx +0 -18
- package/src/components/Graph.tsx +0 -36
- package/src/components/GraphPortal.tsx +0 -22
- package/src/components/Node.spec.tsx +0 -101
- package/src/components/Node.tsx +0 -21
- package/src/components/Subgraph.tsx +0 -30
- package/src/contexts/CurrentGraph.ts +0 -5
- package/src/contexts/GraphContainer.ts +0 -5
- package/src/contexts/GraphMap.ts +0 -5
- package/src/contexts/GraphvizContext.ts +0 -11
- package/src/hooks/__tests__/wrapper.tsx +0 -58
- package/src/hooks/useCurrentGraph.spec.ts +0 -56
- package/src/hooks/useCurrentGraph.ts +0 -18
- package/src/hooks/useDigraph.spec.ts +0 -15
- package/src/hooks/useDigraph.ts +0 -33
- package/src/hooks/useEdge.spec.ts +0 -46
- package/src/hooks/useEdge.ts +0 -35
- package/src/hooks/useGraph.spec.ts +0 -15
- package/src/hooks/useGraph.ts +0 -33
- package/src/hooks/useGraphAttributes.ts +0 -32
- package/src/hooks/useGraphContainer.spec.ts +0 -45
- package/src/hooks/useGraphContainer.ts +0 -10
- package/src/hooks/useGraphMap.ts +0 -7
- package/src/hooks/useGraphvizContext.spec.ts +0 -26
- package/src/hooks/useGraphvizContext.ts +0 -16
- package/src/hooks/useHasAttributes.ts +0 -16
- package/src/hooks/useHasComment.ts +0 -8
- package/src/hooks/useNode.spec.ts +0 -16
- package/src/hooks/useNode.ts +0 -29
- package/src/hooks/useRenderedID.spec.tsx +0 -28
- package/src/hooks/useRenderedID.ts +0 -6
- package/src/hooks/useSubgraph.spec.ts +0 -41
- package/src/hooks/useSubgraph.ts +0 -39
- package/src/react.ts +0 -16
- package/src/reconciler.ts +0 -272
- package/src/render.test.tsx +0 -71
- package/src/render.ts +0 -142
- package/src/renderHTMLLike.ts +0 -9
- package/src/renderId.ts +0 -9
- package/src/types.ts +0 -107
- package/tsconfig.json +0 -8
- package/typedoc.json +0 -5
- package/vite.config.ts +0 -31
package/lib/react.js
CHANGED
|
@@ -1,45 +1,31 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { Digraph as Digraph$1, Graph as Graph$1, Subgraph as Subgraph$1, isRootGraphModel, toDot } from "ts-graphviz";
|
|
2
|
+
import { isAttributeListModel, isEdgeModel, isNodeModel, isRootGraphModel as isRootGraphModel2, isSubgraphModel } from "ts-graphviz";
|
|
3
3
|
import { jsx } from "react/jsx-runtime";
|
|
4
|
-
import {
|
|
4
|
+
import { createContext, useLayoutEffect, useContext, useMemo, useEffect, isValidElement, useImperativeHandle, createElement } from "react";
|
|
5
5
|
import ReactReconciler from "react-reconciler";
|
|
6
|
-
const GraphContainer = createContext(null);
|
|
7
|
-
GraphContainer.displayName = "GraphContainer";
|
|
8
|
-
function useGraphContainer() {
|
|
9
|
-
return useContext(GraphContainer);
|
|
10
|
-
}
|
|
11
6
|
const CurrentGraph = createContext(null);
|
|
12
7
|
CurrentGraph.displayName = "CurrentGraph";
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
if (cluster === null) {
|
|
16
|
-
throw Error(
|
|
17
|
-
"useCurrentGraph must be called within a cluster such as Digraph, Graph, Subgraph."
|
|
18
|
-
);
|
|
19
|
-
}
|
|
20
|
-
return cluster;
|
|
21
|
-
}
|
|
8
|
+
const GraphContainer = createContext(null);
|
|
9
|
+
GraphContainer.displayName = "GraphContainer";
|
|
22
10
|
function useGraphAttributes(cluster, attributes, { edge, node, graph }) {
|
|
23
|
-
|
|
11
|
+
useLayoutEffect(() => {
|
|
24
12
|
cluster.clear();
|
|
25
13
|
cluster.apply(attributes);
|
|
26
14
|
}, [cluster, attributes]);
|
|
27
|
-
|
|
15
|
+
useLayoutEffect(() => {
|
|
28
16
|
cluster.attributes.node.clear();
|
|
29
17
|
cluster.attributes.node.apply(node ?? {});
|
|
30
18
|
}, [cluster, node]);
|
|
31
|
-
|
|
19
|
+
useLayoutEffect(() => {
|
|
32
20
|
cluster.attributes.edge.clear();
|
|
33
21
|
cluster.attributes.edge.apply(edge ?? {});
|
|
34
22
|
}, [cluster, edge]);
|
|
35
|
-
|
|
23
|
+
useLayoutEffect(() => {
|
|
36
24
|
cluster.attributes.graph.clear();
|
|
37
25
|
cluster.attributes.graph.apply(graph ?? {});
|
|
38
26
|
}, [cluster, graph]);
|
|
39
27
|
}
|
|
40
|
-
const GraphvizContext = createContext(
|
|
41
|
-
null
|
|
42
|
-
);
|
|
28
|
+
const GraphvizContext = createContext(null);
|
|
43
29
|
GraphvizContext.displayName = "GraphvizContext";
|
|
44
30
|
function useGraphvizContext() {
|
|
45
31
|
const context = useContext(
|
|
@@ -53,7 +39,7 @@ function useGraphvizContext() {
|
|
|
53
39
|
return context;
|
|
54
40
|
}
|
|
55
41
|
function useHasComment(target, comment) {
|
|
56
|
-
|
|
42
|
+
useLayoutEffect(() => {
|
|
57
43
|
target.comment = comment;
|
|
58
44
|
}, [target, comment]);
|
|
59
45
|
}
|
|
@@ -63,13 +49,8 @@ function useDigraph(options = {}) {
|
|
|
63
49
|
const digraph = useMemo(() => {
|
|
64
50
|
const g = new Digraph$1(id);
|
|
65
51
|
context.container = g;
|
|
66
|
-
g.comment = comment;
|
|
67
|
-
g.apply(attributes);
|
|
68
|
-
g.attributes.node.apply(node ?? {});
|
|
69
|
-
g.attributes.edge.apply(edge ?? {});
|
|
70
|
-
g.attributes.graph.apply(graph ?? {});
|
|
71
52
|
return g;
|
|
72
|
-
}, [context, id
|
|
53
|
+
}, [context, id]);
|
|
73
54
|
useHasComment(digraph, comment);
|
|
74
55
|
useGraphAttributes(digraph, attributes, { edge, node, graph });
|
|
75
56
|
useEffect(() => {
|
|
@@ -79,57 +60,174 @@ function useDigraph(options = {}) {
|
|
|
79
60
|
}, [context]);
|
|
80
61
|
return digraph;
|
|
81
62
|
}
|
|
82
|
-
function
|
|
83
|
-
|
|
84
|
-
const context = useGraphvizContext();
|
|
85
|
-
const memoGraph = useMemo(() => {
|
|
86
|
-
const g = new Graph$1(id);
|
|
87
|
-
context.container = g;
|
|
88
|
-
g.comment = comment;
|
|
89
|
-
g.apply(attributes);
|
|
90
|
-
g.attributes.node.apply(node ?? {});
|
|
91
|
-
g.attributes.edge.apply(edge ?? {});
|
|
92
|
-
g.attributes.graph.apply(graph ?? {});
|
|
93
|
-
return g;
|
|
94
|
-
}, [context, id, comment, edge, node, graph, attributes]);
|
|
95
|
-
useHasComment(memoGraph, comment);
|
|
96
|
-
useGraphAttributes(memoGraph, attributes, { edge, node, graph });
|
|
97
|
-
useEffect(() => {
|
|
98
|
-
return () => {
|
|
99
|
-
context.container = void 0;
|
|
100
|
-
};
|
|
101
|
-
}, [context]);
|
|
102
|
-
return memoGraph;
|
|
63
|
+
function useGraphContainer() {
|
|
64
|
+
return useContext(GraphContainer);
|
|
103
65
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
66
|
+
const GraphMap = createContext(
|
|
67
|
+
null
|
|
68
|
+
);
|
|
69
|
+
GraphMap.displayName = "GraphMap";
|
|
70
|
+
function useGraphMap() {
|
|
71
|
+
const graphMap = useContext(GraphMap);
|
|
72
|
+
if (graphMap === null) {
|
|
73
|
+
throw new Error(
|
|
74
|
+
"useGraphMap must be called within a GraphMap provider. This usually means you are calling it outside of a Graphviz root context."
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
return graphMap;
|
|
78
|
+
}
|
|
79
|
+
const ModelCollectorContext = createContext(null);
|
|
80
|
+
ModelCollectorContext.displayName = "ModelCollectorContext";
|
|
81
|
+
function useModelCollector() {
|
|
82
|
+
return useContext(ModelCollectorContext);
|
|
83
|
+
}
|
|
84
|
+
const DOT_ELEMENT_MAP = {
|
|
85
|
+
"dot:table": "table",
|
|
86
|
+
"dot:tr": "tr",
|
|
87
|
+
"dot:td": "td",
|
|
88
|
+
"dot:font": "font",
|
|
89
|
+
"dot:br": "br",
|
|
90
|
+
"dot:img": "img",
|
|
91
|
+
"dot:i": "i",
|
|
92
|
+
"dot:b": "b",
|
|
93
|
+
"dot:u": "u",
|
|
94
|
+
"dot:o": "o",
|
|
95
|
+
"dot:sub": "sub",
|
|
96
|
+
"dot:sup": "sup",
|
|
97
|
+
"dot:s": "s",
|
|
98
|
+
"dot:hr": "hr",
|
|
99
|
+
"dot:vr": "vr"
|
|
100
|
+
};
|
|
101
|
+
function manuallyRenderElement(element) {
|
|
102
|
+
const { type, props } = element;
|
|
103
|
+
if (typeof type === "function") {
|
|
104
|
+
try {
|
|
105
|
+
const result = typeof type === "function" && type.prototype?.render ? new type(props).render() : type(props);
|
|
106
|
+
if (result && typeof result === "object" && result.type) {
|
|
107
|
+
return manuallyRenderElement(result);
|
|
108
|
+
}
|
|
109
|
+
} catch {
|
|
114
110
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
111
|
+
return "";
|
|
112
|
+
}
|
|
113
|
+
if (typeof type === "string") {
|
|
114
|
+
let tagName = type;
|
|
115
|
+
if (tagName === "dot:port") {
|
|
116
|
+
const propsWithChildren2 = props;
|
|
117
|
+
const childrenString2 = renderChildrenToString(
|
|
118
|
+
propsWithChildren2?.children
|
|
119
|
+
);
|
|
120
|
+
return `<${childrenString2}>`;
|
|
121
|
+
}
|
|
122
|
+
if (tagName.startsWith("dot:")) {
|
|
123
|
+
const htmlElement = DOT_ELEMENT_MAP[tagName];
|
|
124
|
+
if (htmlElement) {
|
|
125
|
+
tagName = htmlElement;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const attributes = [];
|
|
129
|
+
const propsWithChildren = props;
|
|
130
|
+
Object.entries(propsWithChildren || {}).forEach(([key, value]) => {
|
|
131
|
+
if (key !== "children" && value != null) {
|
|
132
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
133
|
+
attributes.push(`${key}="${String(value)}"`);
|
|
134
|
+
} else if (typeof value === "boolean" && value) {
|
|
135
|
+
attributes.push(key);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
const attrsString = attributes.length > 0 ? ` ${attributes.join(" ")}` : "";
|
|
140
|
+
const childrenString = renderChildrenToString(propsWithChildren?.children);
|
|
141
|
+
if (childrenString) {
|
|
142
|
+
return `<${tagName}${attrsString}>${childrenString}</${tagName}>`;
|
|
143
|
+
}
|
|
144
|
+
return `<${tagName}${attrsString}></${tagName}>`;
|
|
145
|
+
}
|
|
146
|
+
return "";
|
|
147
|
+
}
|
|
148
|
+
function renderChildrenToString(children) {
|
|
149
|
+
if (children == null || typeof children === "boolean") {
|
|
150
|
+
return "";
|
|
151
|
+
}
|
|
152
|
+
if (typeof children === "string" || typeof children === "number") {
|
|
153
|
+
return String(children);
|
|
154
|
+
}
|
|
155
|
+
if (Array.isArray(children)) {
|
|
156
|
+
return children.map(renderChildrenToString).join("");
|
|
157
|
+
}
|
|
158
|
+
if (typeof children === "object" && children !== null && "type" in children) {
|
|
159
|
+
return manuallyRenderElement(children);
|
|
160
|
+
}
|
|
161
|
+
return "";
|
|
162
|
+
}
|
|
163
|
+
function renderHTMLLike(label) {
|
|
164
|
+
if (!label) {
|
|
165
|
+
return "<>";
|
|
166
|
+
}
|
|
167
|
+
try {
|
|
168
|
+
const markup = manuallyRenderElement(label);
|
|
169
|
+
if (!markup) {
|
|
170
|
+
return "<>";
|
|
171
|
+
}
|
|
172
|
+
return `<${markup}>`;
|
|
173
|
+
} catch (error) {
|
|
174
|
+
console.warn(
|
|
175
|
+
"renderHTMLLike fallback:",
|
|
176
|
+
error instanceof Error ? error.message : error
|
|
177
|
+
);
|
|
178
|
+
return "<>";
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
function renderId(id) {
|
|
182
|
+
if (isValidElement(id)) {
|
|
183
|
+
return renderHTMLLike(id);
|
|
184
|
+
}
|
|
185
|
+
return id;
|
|
186
|
+
}
|
|
187
|
+
function useRenderedID(id) {
|
|
188
|
+
return useMemo(() => renderId(id), [id]);
|
|
189
|
+
}
|
|
190
|
+
const Digraph = ({
|
|
191
|
+
children,
|
|
192
|
+
label,
|
|
193
|
+
ref,
|
|
194
|
+
...options
|
|
195
|
+
}) => {
|
|
196
|
+
const container = useGraphContainer();
|
|
197
|
+
if (container !== null) {
|
|
198
|
+
throw Error(
|
|
199
|
+
"RootCluster is duplicated.\nUse only one of Digraph and Graph."
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
const renderedLabel = useRenderedID(label);
|
|
203
|
+
if (renderedLabel !== void 0)
|
|
204
|
+
Object.assign(options, { label: renderedLabel });
|
|
205
|
+
const digraph = useDigraph(options);
|
|
206
|
+
const clusters = useGraphMap();
|
|
207
|
+
const modelCollector = useModelCollector();
|
|
208
|
+
useImperativeHandle(ref, () => digraph, [digraph]);
|
|
209
|
+
useLayoutEffect(() => {
|
|
210
|
+
modelCollector?.collectModel(digraph);
|
|
211
|
+
}, [modelCollector, digraph]);
|
|
124
212
|
useEffect(() => {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
128
|
-
}, [
|
|
129
|
-
return
|
|
213
|
+
if (digraph.id !== void 0) {
|
|
214
|
+
clusters.set(digraph.id, digraph);
|
|
215
|
+
}
|
|
216
|
+
}, [clusters, digraph]);
|
|
217
|
+
return /* @__PURE__ */ jsx(GraphContainer.Provider, { value: digraph, children: /* @__PURE__ */ jsx(CurrentGraph.Provider, { value: digraph, children }) });
|
|
218
|
+
};
|
|
219
|
+
Digraph.displayName = "Digraph";
|
|
220
|
+
function useCurrentGraph() {
|
|
221
|
+
const cluster = useContext(CurrentGraph);
|
|
222
|
+
if (cluster === null) {
|
|
223
|
+
throw Error(
|
|
224
|
+
"useCurrentGraph must be called within a cluster such as Digraph, Graph, Subgraph."
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
return cluster;
|
|
130
228
|
}
|
|
131
229
|
function useHasAttributes(target, attributes) {
|
|
132
|
-
|
|
230
|
+
useLayoutEffect(() => {
|
|
133
231
|
target.attributes.clear();
|
|
134
232
|
target.attributes.apply(attributes);
|
|
135
233
|
}, [target, attributes]);
|
|
@@ -142,10 +240,8 @@ function useEdge(targets, props = {}) {
|
|
|
142
240
|
}
|
|
143
241
|
const edge = useMemo(() => {
|
|
144
242
|
const e = cluster.createEdge(targets);
|
|
145
|
-
e.comment = comment;
|
|
146
|
-
e.attributes.apply(attributes);
|
|
147
243
|
return e;
|
|
148
|
-
}, [cluster, targets
|
|
244
|
+
}, [cluster, targets]);
|
|
149
245
|
useHasComment(edge, comment);
|
|
150
246
|
useHasAttributes(edge, attributes);
|
|
151
247
|
useEffect(() => {
|
|
@@ -155,43 +251,42 @@ function useEdge(targets, props = {}) {
|
|
|
155
251
|
}, [cluster, edge]);
|
|
156
252
|
return edge;
|
|
157
253
|
}
|
|
158
|
-
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
254
|
+
const Edge = ({ targets, label, ref, ...options }) => {
|
|
255
|
+
const renderedLabel = useRenderedID(label);
|
|
256
|
+
if (renderedLabel !== void 0)
|
|
257
|
+
Object.assign(options, { label: renderedLabel });
|
|
258
|
+
const edge = useEdge(targets, options);
|
|
259
|
+
const modelCollector = useModelCollector();
|
|
260
|
+
useImperativeHandle(ref, () => edge, [edge]);
|
|
261
|
+
useLayoutEffect(() => {
|
|
262
|
+
modelCollector?.collectModel(edge);
|
|
263
|
+
}, [modelCollector, edge]);
|
|
264
|
+
return null;
|
|
265
|
+
};
|
|
266
|
+
Edge.displayName = "Edge";
|
|
267
|
+
function useGraph(options = {}) {
|
|
268
|
+
const { id, comment, edge, node, graph, ...attributes } = options;
|
|
269
|
+
const context = useGraphvizContext();
|
|
270
|
+
const memoGraph = useMemo(() => {
|
|
271
|
+
const g = new Graph$1(id);
|
|
272
|
+
context.container = g;
|
|
273
|
+
return g;
|
|
274
|
+
}, [context, id]);
|
|
275
|
+
useHasComment(memoGraph, comment);
|
|
276
|
+
useGraphAttributes(memoGraph, attributes, { edge, node, graph });
|
|
169
277
|
useEffect(() => {
|
|
170
278
|
return () => {
|
|
171
|
-
|
|
279
|
+
context.container = void 0;
|
|
172
280
|
};
|
|
173
|
-
}, [
|
|
174
|
-
return
|
|
175
|
-
}
|
|
176
|
-
const GraphMap = createContext(/* @__PURE__ */ new Map());
|
|
177
|
-
GraphMap.displayName = "GraphMap";
|
|
178
|
-
function useGraphMap() {
|
|
179
|
-
return useContext(GraphMap);
|
|
180
|
-
}
|
|
181
|
-
function renderHTMLLike(label) {
|
|
182
|
-
const htmlLike = renderToStaticMarkup(label).replace(/<dot:port>(.+?)<\/dot:port>/gi, "<$1>").replace(/<(\/?)dot:([a-z-]+)/gi, (_, $1, $2) => `<${$1}${$2}`);
|
|
183
|
-
return `<${htmlLike}>`;
|
|
184
|
-
}
|
|
185
|
-
function renderId(id) {
|
|
186
|
-
if (isValidElement(id)) {
|
|
187
|
-
return renderHTMLLike(id);
|
|
188
|
-
}
|
|
189
|
-
return id;
|
|
190
|
-
}
|
|
191
|
-
function useRenderedID(id) {
|
|
192
|
-
return useMemo(() => renderId(id), [id]);
|
|
281
|
+
}, [context]);
|
|
282
|
+
return memoGraph;
|
|
193
283
|
}
|
|
194
|
-
const Graph = ({
|
|
284
|
+
const Graph = ({
|
|
285
|
+
children,
|
|
286
|
+
label,
|
|
287
|
+
ref,
|
|
288
|
+
...options
|
|
289
|
+
}) => {
|
|
195
290
|
const container = useGraphContainer();
|
|
196
291
|
if (container !== null) {
|
|
197
292
|
throw Error(
|
|
@@ -203,6 +298,11 @@ const Graph = ({ children, label, ...options }) => {
|
|
|
203
298
|
Object.assign(options, { label: renderedLabel });
|
|
204
299
|
const graph = useGraph(options);
|
|
205
300
|
const clusters = useGraphMap();
|
|
301
|
+
const modelCollector = useModelCollector();
|
|
302
|
+
useImperativeHandle(ref, () => graph, [graph]);
|
|
303
|
+
useLayoutEffect(() => {
|
|
304
|
+
modelCollector?.collectModel(graph);
|
|
305
|
+
}, [modelCollector, graph]);
|
|
206
306
|
useEffect(() => {
|
|
207
307
|
if (graph.id !== void 0) {
|
|
208
308
|
clusters.set(graph.id, graph);
|
|
@@ -211,33 +311,74 @@ const Graph = ({ children, label, ...options }) => {
|
|
|
211
311
|
return /* @__PURE__ */ jsx(GraphContainer.Provider, { value: graph, children: /* @__PURE__ */ jsx(CurrentGraph.Provider, { value: graph, children }) });
|
|
212
312
|
};
|
|
213
313
|
Graph.displayName = "Graph";
|
|
214
|
-
const
|
|
215
|
-
children,
|
|
216
|
-
label,
|
|
217
|
-
...options
|
|
218
|
-
}) => {
|
|
314
|
+
const GraphPortal = ({ children, id }) => {
|
|
219
315
|
const container = useGraphContainer();
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
316
|
+
const map = useContext(GraphMap);
|
|
317
|
+
const cluster = useMemo(
|
|
318
|
+
() => id && map ? map.get(id) ?? container : container,
|
|
319
|
+
[container, map, id]
|
|
320
|
+
);
|
|
321
|
+
return /* @__PURE__ */ jsx(CurrentGraph.Provider, { value: cluster, children });
|
|
322
|
+
};
|
|
323
|
+
GraphPortal.displayName = "GraphPortal";
|
|
324
|
+
function useNode(id, options = {}) {
|
|
325
|
+
const { comment, ...attributes } = options;
|
|
326
|
+
const cluster = useCurrentGraph();
|
|
327
|
+
const node = useMemo(() => {
|
|
328
|
+
const n = cluster.createNode(id);
|
|
329
|
+
return n;
|
|
330
|
+
}, [cluster, id]);
|
|
331
|
+
useHasComment(node, comment);
|
|
332
|
+
useHasAttributes(node, attributes);
|
|
333
|
+
useEffect(() => {
|
|
334
|
+
return () => {
|
|
335
|
+
cluster.removeNode(node);
|
|
336
|
+
};
|
|
337
|
+
}, [cluster, node]);
|
|
338
|
+
return node;
|
|
339
|
+
}
|
|
340
|
+
const Node = ({ id, label, xlabel, ref, ...options }) => {
|
|
225
341
|
const renderedLabel = useRenderedID(label);
|
|
342
|
+
const renderedXlabel = useRenderedID(xlabel);
|
|
226
343
|
if (renderedLabel !== void 0)
|
|
227
344
|
Object.assign(options, { label: renderedLabel });
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
345
|
+
if (renderedXlabel !== void 0)
|
|
346
|
+
Object.assign(options, { xlabel: renderedXlabel });
|
|
347
|
+
const node = useNode(id, options);
|
|
348
|
+
const modelCollector = useModelCollector();
|
|
349
|
+
useImperativeHandle(ref, () => node, [node]);
|
|
350
|
+
useLayoutEffect(() => {
|
|
351
|
+
modelCollector?.collectModel(node);
|
|
352
|
+
}, [modelCollector, node]);
|
|
353
|
+
return null;
|
|
236
354
|
};
|
|
237
|
-
|
|
355
|
+
Node.displayName = "Node";
|
|
356
|
+
function useSubgraph(props = {}) {
|
|
357
|
+
const { id, comment, edge, node, graph, ...attributes } = props;
|
|
358
|
+
const context = useGraphvizContext();
|
|
359
|
+
const cluster = useCurrentGraph();
|
|
360
|
+
const subgraph = useMemo(() => {
|
|
361
|
+
const g = new Subgraph$1(id);
|
|
362
|
+
if (cluster !== null) {
|
|
363
|
+
cluster.addSubgraph(g);
|
|
364
|
+
} else if (!context.container) {
|
|
365
|
+
context.container = g;
|
|
366
|
+
}
|
|
367
|
+
return g;
|
|
368
|
+
}, [context, cluster, id]);
|
|
369
|
+
useHasComment(subgraph, comment);
|
|
370
|
+
useGraphAttributes(subgraph, attributes, { edge, node, graph });
|
|
371
|
+
useEffect(() => {
|
|
372
|
+
return () => {
|
|
373
|
+
cluster.removeSubgraph(subgraph);
|
|
374
|
+
};
|
|
375
|
+
}, [cluster, subgraph]);
|
|
376
|
+
return subgraph;
|
|
377
|
+
}
|
|
238
378
|
const Subgraph = ({
|
|
239
379
|
children,
|
|
240
380
|
label,
|
|
381
|
+
ref,
|
|
241
382
|
...options
|
|
242
383
|
}) => {
|
|
243
384
|
const renderedLabel = useRenderedID(label);
|
|
@@ -245,6 +386,11 @@ const Subgraph = ({
|
|
|
245
386
|
Object.assign(options, { label: renderedLabel });
|
|
246
387
|
const subgraph = useSubgraph(options);
|
|
247
388
|
const clusters = useGraphMap();
|
|
389
|
+
const modelCollector = useModelCollector();
|
|
390
|
+
useImperativeHandle(ref, () => subgraph, [subgraph]);
|
|
391
|
+
useLayoutEffect(() => {
|
|
392
|
+
modelCollector?.collectModel(subgraph);
|
|
393
|
+
}, [modelCollector, subgraph]);
|
|
248
394
|
useEffect(() => {
|
|
249
395
|
if (subgraph.id !== void 0) {
|
|
250
396
|
clusters.set(subgraph.id, subgraph);
|
|
@@ -253,43 +399,80 @@ const Subgraph = ({
|
|
|
253
399
|
return /* @__PURE__ */ jsx(CurrentGraph.Provider, { value: subgraph, children });
|
|
254
400
|
};
|
|
255
401
|
Subgraph.displayName = "Subgraph";
|
|
256
|
-
const Node = ({ id, label, xlabel, ...options }) => {
|
|
257
|
-
const renderedLabel = useRenderedID(label);
|
|
258
|
-
const renderedXlabel = useRenderedID(xlabel);
|
|
259
|
-
if (renderedLabel !== void 0)
|
|
260
|
-
Object.assign(options, { label: renderedLabel });
|
|
261
|
-
if (renderedXlabel !== void 0)
|
|
262
|
-
Object.assign(options, { xlabel: renderedXlabel });
|
|
263
|
-
useNode(id, options);
|
|
264
|
-
return null;
|
|
265
|
-
};
|
|
266
|
-
Node.displayName = "Node";
|
|
267
|
-
const Edge = ({ targets, label, ...options }) => {
|
|
268
|
-
const renderedLabel = useRenderedID(label);
|
|
269
|
-
if (renderedLabel !== void 0)
|
|
270
|
-
Object.assign(options, { label: renderedLabel });
|
|
271
|
-
useEdge(targets, options);
|
|
272
|
-
return null;
|
|
273
|
-
};
|
|
274
|
-
Edge.displayName = "Edge";
|
|
275
|
-
const GraphPortal = ({ children, id }) => {
|
|
276
|
-
const container = useGraphContainer();
|
|
277
|
-
const map = useContext(GraphMap);
|
|
278
|
-
const cluster = useMemo(
|
|
279
|
-
() => id ? map.get(id) ?? container : container,
|
|
280
|
-
[container, map, id]
|
|
281
|
-
);
|
|
282
|
-
return /* @__PURE__ */ jsx(CurrentGraph.Provider, { value: cluster, children });
|
|
283
|
-
};
|
|
284
|
-
GraphPortal.displayName = "GraphPortal";
|
|
285
402
|
class HostConfig {
|
|
286
|
-
preparePortalMount(
|
|
403
|
+
preparePortalMount(_containerInfo) {
|
|
287
404
|
}
|
|
288
405
|
scheduleTimeout(fn, delay) {
|
|
406
|
+
return setTimeout(fn, delay || 0);
|
|
289
407
|
}
|
|
290
408
|
cancelTimeout(id) {
|
|
409
|
+
clearTimeout(id);
|
|
291
410
|
}
|
|
292
411
|
queueMicrotask(fn) {
|
|
412
|
+
fn();
|
|
413
|
+
}
|
|
414
|
+
// For update priority management
|
|
415
|
+
resolveUpdatePriority() {
|
|
416
|
+
return 1;
|
|
417
|
+
}
|
|
418
|
+
setCurrentUpdatePriority(_priority) {
|
|
419
|
+
}
|
|
420
|
+
getCurrentUpdatePriority() {
|
|
421
|
+
return 1;
|
|
422
|
+
}
|
|
423
|
+
// form-related methods
|
|
424
|
+
resetFormInstance(_form) {
|
|
425
|
+
}
|
|
426
|
+
// transition-related properties
|
|
427
|
+
NotPendingTransition = null;
|
|
428
|
+
// suspense and commit methods
|
|
429
|
+
maySuspendCommit(_type, _props) {
|
|
430
|
+
return false;
|
|
431
|
+
}
|
|
432
|
+
preloadInstance(_type, _props) {
|
|
433
|
+
return false;
|
|
434
|
+
}
|
|
435
|
+
startSuspendingCommit() {
|
|
436
|
+
}
|
|
437
|
+
suspendInstance(_type, _props) {
|
|
438
|
+
}
|
|
439
|
+
waitForCommitToBeReady() {
|
|
440
|
+
return null;
|
|
441
|
+
}
|
|
442
|
+
// additional required methods
|
|
443
|
+
getInstanceFromNode(_node) {
|
|
444
|
+
return null;
|
|
445
|
+
}
|
|
446
|
+
beforeActiveInstanceBlur() {
|
|
447
|
+
}
|
|
448
|
+
afterActiveInstanceBlur() {
|
|
449
|
+
}
|
|
450
|
+
prepareScopeUpdate(_scopeInstance, _instance) {
|
|
451
|
+
}
|
|
452
|
+
getInstanceFromScope(_scopeInstance) {
|
|
453
|
+
return null;
|
|
454
|
+
}
|
|
455
|
+
detachDeletedInstance(_instance) {
|
|
456
|
+
}
|
|
457
|
+
logRecoverableError(_error) {
|
|
458
|
+
}
|
|
459
|
+
requestPostPaintCallback(_callback) {
|
|
460
|
+
}
|
|
461
|
+
// container clearing method
|
|
462
|
+
clearContainer(_container) {
|
|
463
|
+
}
|
|
464
|
+
// transition context and scheduling methods
|
|
465
|
+
HostTransitionContext = null;
|
|
466
|
+
shouldAttemptEagerTransition() {
|
|
467
|
+
return false;
|
|
468
|
+
}
|
|
469
|
+
trackSchedulerEvent() {
|
|
470
|
+
}
|
|
471
|
+
resolveEventType() {
|
|
472
|
+
return null;
|
|
473
|
+
}
|
|
474
|
+
resolveEventTimeStamp() {
|
|
475
|
+
return Date.now();
|
|
293
476
|
}
|
|
294
477
|
cloneInstance;
|
|
295
478
|
cloneFundamentalInstance;
|
|
@@ -307,51 +490,75 @@ class HostConfig {
|
|
|
307
490
|
// Temporary workaround for scenario where multiple renderers concurrently
|
|
308
491
|
// render using the same context objects. E.g. React DOM and React ART on the
|
|
309
492
|
// same page. DOM is the primary renderer; ART is the secondary renderer.
|
|
310
|
-
isPrimaryRenderer =
|
|
311
|
-
supportsMutation =
|
|
493
|
+
isPrimaryRenderer = true;
|
|
494
|
+
supportsMutation = true;
|
|
312
495
|
supportsPersistence = false;
|
|
313
496
|
supportsHydration = false;
|
|
497
|
+
// specific configurations for synchronous rendering
|
|
498
|
+
supportsMicrotasks = false;
|
|
314
499
|
getPublicInstance(instance) {
|
|
315
500
|
return instance;
|
|
316
501
|
}
|
|
317
|
-
getRootHostContext(
|
|
318
|
-
return {
|
|
502
|
+
getRootHostContext(_rootContainerInstance) {
|
|
503
|
+
return {
|
|
504
|
+
graph: void 0
|
|
505
|
+
};
|
|
319
506
|
}
|
|
320
|
-
getChildHostContext(parentHostContext,
|
|
321
|
-
return
|
|
507
|
+
getChildHostContext(parentHostContext, _type, _rootContainerInstance) {
|
|
508
|
+
return {
|
|
509
|
+
...parentHostContext
|
|
510
|
+
// You could extend context based on component type if needed
|
|
511
|
+
};
|
|
322
512
|
}
|
|
323
|
-
prepareForCommit(
|
|
513
|
+
prepareForCommit(_containerInfo) {
|
|
324
514
|
return null;
|
|
325
515
|
}
|
|
326
|
-
resetAfterCommit(
|
|
516
|
+
resetAfterCommit(_containerInfo) {
|
|
327
517
|
}
|
|
328
518
|
/**
|
|
329
|
-
* Create component instance
|
|
519
|
+
* Create component instance for Graphviz elements
|
|
330
520
|
*/
|
|
331
|
-
createInstance(type, props,
|
|
332
|
-
|
|
521
|
+
createInstance(type, props, _rootContainerInstance, _hostContext, _internalInstanceHandle) {
|
|
522
|
+
const instance = {
|
|
523
|
+
type: type.displayName || type.name || "Unknown",
|
|
524
|
+
props,
|
|
525
|
+
children: [],
|
|
526
|
+
appendChild: (child) => {
|
|
527
|
+
instance.children.push(child);
|
|
528
|
+
}
|
|
529
|
+
};
|
|
530
|
+
return instance;
|
|
333
531
|
}
|
|
334
532
|
appendInitialChild(parentInstance, child) {
|
|
335
|
-
parentInstance.appendChild
|
|
533
|
+
if (parentInstance.appendChild) {
|
|
534
|
+
parentInstance.appendChild(child);
|
|
535
|
+
}
|
|
336
536
|
}
|
|
337
|
-
finalizeInitialChildren(
|
|
537
|
+
finalizeInitialChildren(_instance, _type, _props, _rootContainerInstance, _hostContext) {
|
|
338
538
|
return false;
|
|
339
539
|
}
|
|
340
|
-
prepareUpdate(
|
|
341
|
-
|
|
540
|
+
prepareUpdate(_instance, _type, oldProps, newProps, _rootContainerInstance, _hostContext) {
|
|
541
|
+
if (JSON.stringify(oldProps) !== JSON.stringify(newProps)) {
|
|
542
|
+
return {
|
|
543
|
+
type: "UPDATE",
|
|
544
|
+
oldProps,
|
|
545
|
+
newProps
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
return null;
|
|
342
549
|
}
|
|
343
|
-
shouldSetTextContent(
|
|
550
|
+
shouldSetTextContent(_type, _props) {
|
|
344
551
|
return false;
|
|
345
552
|
}
|
|
346
|
-
shouldDeprioritizeSubtree(
|
|
553
|
+
shouldDeprioritizeSubtree(_type, _props) {
|
|
347
554
|
return false;
|
|
348
555
|
}
|
|
349
|
-
createTextInstance(text,
|
|
350
|
-
return text;
|
|
556
|
+
createTextInstance(text, _rootContainerInstance, _hostContext, _internalInstanceHandle) {
|
|
557
|
+
return text.trim();
|
|
351
558
|
}
|
|
352
|
-
scheduleDeferredCallback(
|
|
559
|
+
scheduleDeferredCallback(_callback, _options) {
|
|
353
560
|
}
|
|
354
|
-
cancelDeferredCallback(
|
|
561
|
+
cancelDeferredCallback(_callbackID) {
|
|
355
562
|
}
|
|
356
563
|
// -------------------
|
|
357
564
|
// Mutation
|
|
@@ -363,22 +570,26 @@ class HostConfig {
|
|
|
363
570
|
}
|
|
364
571
|
}
|
|
365
572
|
appendChildToContainer(container, child) {
|
|
573
|
+
if (typeof child === "object" && "type" in child) {
|
|
574
|
+
container.__rootInstance = child;
|
|
575
|
+
}
|
|
366
576
|
}
|
|
367
|
-
commitTextUpdate(
|
|
577
|
+
commitTextUpdate(_textInstance, _oldText, _newText) {
|
|
368
578
|
}
|
|
369
|
-
commitMount(
|
|
579
|
+
commitMount(_instance, _type, _props, _internalInstanceHandle) {
|
|
370
580
|
}
|
|
371
|
-
commitUpdate(instance,
|
|
581
|
+
commitUpdate(instance, _type, _prevProps, nextProps, _internalHandle) {
|
|
582
|
+
instance.props = nextProps;
|
|
372
583
|
}
|
|
373
|
-
insertBefore(
|
|
584
|
+
insertBefore(_parentInstance, _child, _beforeChild) {
|
|
374
585
|
}
|
|
375
|
-
insertInContainerBefore(
|
|
586
|
+
insertInContainerBefore(_container, _child, _beforeChild) {
|
|
376
587
|
}
|
|
377
|
-
removeChild(
|
|
588
|
+
removeChild(_parentInstance, _child) {
|
|
378
589
|
}
|
|
379
|
-
removeChildFromContainer(
|
|
590
|
+
removeChildFromContainer(_container, _child) {
|
|
380
591
|
}
|
|
381
|
-
resetTextContent(
|
|
592
|
+
resetTextContent(_instance) {
|
|
382
593
|
}
|
|
383
594
|
}
|
|
384
595
|
const reconciler = ReactReconciler(new HostConfig());
|
|
@@ -394,12 +605,13 @@ function clusterMap(graph, map = /* @__PURE__ */ new Map()) {
|
|
|
394
605
|
}
|
|
395
606
|
return map;
|
|
396
607
|
}
|
|
397
|
-
function
|
|
398
|
-
|
|
399
|
-
|
|
608
|
+
function createWrappedElement(element, context, collectModel, container) {
|
|
609
|
+
return createElement(
|
|
610
|
+
GraphvizContext.Provider,
|
|
611
|
+
{ value: context },
|
|
400
612
|
createElement(
|
|
401
|
-
|
|
402
|
-
{ value:
|
|
613
|
+
ModelCollectorContext.Provider,
|
|
614
|
+
{ value: { collectModel } },
|
|
403
615
|
createElement(
|
|
404
616
|
GraphMap.Provider,
|
|
405
617
|
{ value: clusterMap(container) },
|
|
@@ -413,29 +625,155 @@ function render(element, container) {
|
|
|
413
625
|
) : element
|
|
414
626
|
)
|
|
415
627
|
)
|
|
416
|
-
)
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
628
|
+
)
|
|
629
|
+
);
|
|
630
|
+
}
|
|
631
|
+
function createFiberRoot(containerInfo, options = {}) {
|
|
632
|
+
const { onUncaughtError = noop } = options;
|
|
633
|
+
return reconciler.createContainer(
|
|
634
|
+
containerInfo,
|
|
635
|
+
1,
|
|
636
|
+
// tag - ConcurrentRoot = 1 for concurrent mode (React 19 default)
|
|
637
|
+
null,
|
|
638
|
+
// hydrationCallbacks
|
|
639
|
+
false,
|
|
640
|
+
// isStrictMode
|
|
427
641
|
null,
|
|
428
|
-
|
|
642
|
+
// concurrentUpdatesByDefaultOverride
|
|
643
|
+
"@ts-graphviz/react",
|
|
644
|
+
// identifierPrefix
|
|
645
|
+
// Enhance onRecoverableError to forward componentStack for better diagnostics
|
|
646
|
+
onUncaughtError ? (error, errorInfo = {}) => onUncaughtError(error, errorInfo) : noop,
|
|
647
|
+
// onRecoverableError
|
|
648
|
+
null
|
|
649
|
+
// transitionCallbacks
|
|
429
650
|
);
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
651
|
+
}
|
|
652
|
+
function validateModels(models, container) {
|
|
653
|
+
if (container) {
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
const topLevelGraphs = models.filter((model) => model.$$type === "Graph");
|
|
657
|
+
if (topLevelGraphs.length === 0) {
|
|
658
|
+
throw new Error(
|
|
659
|
+
"No top-level graph found. When not using a container, you must provide exactly one Graph or Digraph component at the top level."
|
|
433
660
|
);
|
|
434
661
|
}
|
|
435
|
-
|
|
662
|
+
if (topLevelGraphs.length > 1) {
|
|
663
|
+
throw new Error(
|
|
664
|
+
`Multiple top-level graphs detected (${topLevelGraphs.length}). DOT language only supports one graph per file. Consider using a container or rendering graphs separately.`
|
|
665
|
+
);
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
async function renderInternal(containerInfo, fiberRoot, wrappedElement, originalContainer, options = {}) {
|
|
669
|
+
const { timeout = 5e3, onRenderComplete } = options;
|
|
670
|
+
return new Promise((resolve, reject) => {
|
|
671
|
+
const timeoutId = setTimeout(() => {
|
|
672
|
+
reject(new Error(`Rendering timeout after ${timeout}ms`));
|
|
673
|
+
}, timeout);
|
|
674
|
+
try {
|
|
675
|
+
reconciler.updateContainer(wrappedElement, fiberRoot, null, () => {
|
|
676
|
+
clearTimeout(timeoutId);
|
|
677
|
+
try {
|
|
678
|
+
validateModels(containerInfo.renderedModels, originalContainer);
|
|
679
|
+
if (onRenderComplete) {
|
|
680
|
+
onRenderComplete([...containerInfo.renderedModels]);
|
|
681
|
+
}
|
|
682
|
+
resolve();
|
|
683
|
+
} catch (error) {
|
|
684
|
+
reject(error);
|
|
685
|
+
}
|
|
686
|
+
});
|
|
687
|
+
} catch (error) {
|
|
688
|
+
clearTimeout(timeoutId);
|
|
689
|
+
reject(error);
|
|
690
|
+
}
|
|
691
|
+
});
|
|
436
692
|
}
|
|
437
|
-
function
|
|
438
|
-
|
|
693
|
+
function createRoot(options = {}) {
|
|
694
|
+
const { container, ...otherOptions } = options;
|
|
695
|
+
const containerInfo = {
|
|
696
|
+
renderedModels: []
|
|
697
|
+
};
|
|
698
|
+
const collectModel = (model) => {
|
|
699
|
+
if (container) {
|
|
700
|
+
if (model !== container) {
|
|
701
|
+
containerInfo.renderedModels.push(model);
|
|
702
|
+
}
|
|
703
|
+
} else {
|
|
704
|
+
if (isRootGraphModel(model)) {
|
|
705
|
+
containerInfo.renderedModels.push(model);
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
};
|
|
709
|
+
const context = {
|
|
710
|
+
container
|
|
711
|
+
};
|
|
712
|
+
containerInfo.context = context;
|
|
713
|
+
const fiberRoot = createFiberRoot(containerInfo, otherOptions);
|
|
714
|
+
let isUnmounted = false;
|
|
715
|
+
return {
|
|
716
|
+
async render(element) {
|
|
717
|
+
if (isUnmounted) {
|
|
718
|
+
throw new Error("Cannot render on an unmounted root");
|
|
719
|
+
}
|
|
720
|
+
containerInfo.renderedModels = [];
|
|
721
|
+
const wrappedElement = createWrappedElement(
|
|
722
|
+
element,
|
|
723
|
+
context,
|
|
724
|
+
collectModel,
|
|
725
|
+
container
|
|
726
|
+
);
|
|
727
|
+
return renderInternal(
|
|
728
|
+
containerInfo,
|
|
729
|
+
fiberRoot,
|
|
730
|
+
wrappedElement,
|
|
731
|
+
container,
|
|
732
|
+
otherOptions
|
|
733
|
+
);
|
|
734
|
+
},
|
|
735
|
+
unmount() {
|
|
736
|
+
if (isUnmounted) {
|
|
737
|
+
return;
|
|
738
|
+
}
|
|
739
|
+
isUnmounted = true;
|
|
740
|
+
reconciler.updateContainer(null, fiberRoot, null, noop);
|
|
741
|
+
containerInfo.renderedModels = [];
|
|
742
|
+
},
|
|
743
|
+
getTopLevelModels(typeGuard) {
|
|
744
|
+
const models = [...containerInfo.renderedModels];
|
|
745
|
+
if (typeGuard) {
|
|
746
|
+
return models.filter(typeGuard);
|
|
747
|
+
}
|
|
748
|
+
return models;
|
|
749
|
+
}
|
|
750
|
+
};
|
|
751
|
+
}
|
|
752
|
+
async function renderToDot(element, options = {}) {
|
|
753
|
+
const { container, ...rootOptions } = options;
|
|
754
|
+
if (container) {
|
|
755
|
+
const root = createRoot({ container, ...rootOptions });
|
|
756
|
+
try {
|
|
757
|
+
await root.render(element);
|
|
758
|
+
return toDot(container);
|
|
759
|
+
} finally {
|
|
760
|
+
root.unmount();
|
|
761
|
+
}
|
|
762
|
+
} else {
|
|
763
|
+
const root = createRoot(rootOptions);
|
|
764
|
+
try {
|
|
765
|
+
await root.render(element);
|
|
766
|
+
const models = root.getTopLevelModels();
|
|
767
|
+
if (models.length !== 1) {
|
|
768
|
+
throw new Error(
|
|
769
|
+
`Expected exactly one top-level graph, but got ${models.length}. Use a container or ensure only one Graph/Digraph component is at the top level.`
|
|
770
|
+
);
|
|
771
|
+
}
|
|
772
|
+
return toDot(models[0]);
|
|
773
|
+
} finally {
|
|
774
|
+
root.unmount();
|
|
775
|
+
}
|
|
776
|
+
}
|
|
439
777
|
}
|
|
440
778
|
export {
|
|
441
779
|
Digraph,
|
|
@@ -444,7 +782,12 @@ export {
|
|
|
444
782
|
GraphPortal,
|
|
445
783
|
Node,
|
|
446
784
|
Subgraph,
|
|
447
|
-
|
|
785
|
+
createRoot,
|
|
786
|
+
isAttributeListModel,
|
|
787
|
+
isEdgeModel,
|
|
788
|
+
isNodeModel,
|
|
789
|
+
isRootGraphModel2 as isRootGraphModel,
|
|
790
|
+
isSubgraphModel,
|
|
448
791
|
renderHTMLLike,
|
|
449
792
|
renderToDot,
|
|
450
793
|
useCurrentGraph,
|