@hpcc-js/graph 2.70.0 → 2.74.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/dist/index.es6.js +424 -26
- package/dist/index.es6.js.map +1 -1
- package/dist/index.js +425 -25
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/package.json +8 -7
- package/src/Sankey.ts +35 -14
- package/src/__package__.ts +2 -2
- package/src/graph2/dataGraph.ts +34 -13
- package/src/graph2/graph.ts +38 -30
- package/src/graph2/index.ts +1 -0
- package/src/graph2/layouts/graphviz.ts +33 -1
- package/src/graph2/layouts/placeholders.ts +1 -0
- package/src/graph2/sankeyGraph.css +45 -0
- package/src/graph2/sankeyGraph.ts +322 -0
- package/src/test.ts +49 -4
- package/types/Sankey.d.ts +8 -0
- package/types/Sankey.d.ts.map +1 -1
- package/types/__package__.d.ts +2 -2
- package/types/graph2/dataGraph.d.ts +3 -0
- package/types/graph2/dataGraph.d.ts.map +1 -1
- package/types/graph2/graph.d.ts +3 -1
- package/types/graph2/graph.d.ts.map +1 -1
- package/types/graph2/index.d.ts +1 -0
- package/types/graph2/index.d.ts.map +1 -1
- package/types/graph2/layouts/graphviz.d.ts +6 -1
- package/types/graph2/layouts/graphviz.d.ts.map +1 -1
- package/types/graph2/layouts/placeholders.d.ts +1 -0
- package/types/graph2/layouts/placeholders.d.ts.map +1 -1
- package/types/graph2/sankeyGraph.d.ts +73 -0
- package/types/graph2/sankeyGraph.d.ts.map +1 -0
- package/types/test.d.ts +6 -1
- package/types/test.d.ts.map +1 -1
- package/types-3.4/Sankey.d.ts +8 -0
- package/types-3.4/__package__.d.ts +2 -2
- package/types-3.4/graph2/dataGraph.d.ts +3 -0
- package/types-3.4/graph2/graph.d.ts +3 -1
- package/types-3.4/graph2/index.d.ts +1 -0
- package/types-3.4/graph2/layouts/graphviz.d.ts +6 -1
- package/types-3.4/graph2/layouts/placeholders.d.ts +1 -0
- package/types-3.4/graph2/sankeyGraph.d.ts +73 -0
- package/types-3.4/test.d.ts +6 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hpcc-js/graph",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.74.0",
|
|
4
4
|
"description": "hpcc-js - Viz Graph",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.es6",
|
|
@@ -51,11 +51,11 @@
|
|
|
51
51
|
"ddd": "downlevel-dts . ts3.4"
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
|
-
"@hpcc-js/api": "^2.8.
|
|
55
|
-
"@hpcc-js/common": "^2.
|
|
56
|
-
"@hpcc-js/html": "^2.
|
|
57
|
-
"@hpcc-js/react": "^2.
|
|
58
|
-
"@hpcc-js/util": "^2.
|
|
54
|
+
"@hpcc-js/api": "^2.8.56",
|
|
55
|
+
"@hpcc-js/common": "^2.61.0",
|
|
56
|
+
"@hpcc-js/html": "^2.36.0",
|
|
57
|
+
"@hpcc-js/react": "^2.44.0",
|
|
58
|
+
"@hpcc-js/util": "^2.41.0"
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
61
61
|
"@hpcc-js/bundle": "^2.10.20",
|
|
@@ -82,6 +82,7 @@
|
|
|
82
82
|
"downlevel-dts": "0.6.0",
|
|
83
83
|
"es6-promise": "4.2.8",
|
|
84
84
|
"eslint": "7.32.0",
|
|
85
|
+
"eslint-plugin-react-hooks": "^4.3.0",
|
|
85
86
|
"npm-run-all": "4.1.5",
|
|
86
87
|
"rimraf": "2.6.3",
|
|
87
88
|
"rollup": "2.10.7",
|
|
@@ -103,5 +104,5 @@
|
|
|
103
104
|
"url": "https://github.com/hpcc-systems/Visualization/issues"
|
|
104
105
|
},
|
|
105
106
|
"homepage": "https://github.com/hpcc-systems/Visualization",
|
|
106
|
-
"gitHead": "
|
|
107
|
+
"gitHead": "0ecc9bdf1688d237242e998f5c23e4355ee0586d"
|
|
107
108
|
}
|
package/src/Sankey.ts
CHANGED
|
@@ -68,7 +68,6 @@ export class Sankey extends SVGWidget {
|
|
|
68
68
|
constructor() {
|
|
69
69
|
super();
|
|
70
70
|
Utility.SimpleSelectionMixin.call(this);
|
|
71
|
-
|
|
72
71
|
this._drawStartPos = "origin";
|
|
73
72
|
}
|
|
74
73
|
|
|
@@ -79,6 +78,7 @@ export class Sankey extends SVGWidget {
|
|
|
79
78
|
};
|
|
80
79
|
if (this.data().length === 0) return retVal;
|
|
81
80
|
const vertexIndex = {};
|
|
81
|
+
const valueIdx = 2;
|
|
82
82
|
const mappings = this.mappings().filter(mapping => mapping.valid());
|
|
83
83
|
mappings.forEach(function (mapping, idx) {
|
|
84
84
|
const view = this._db.rollupView([mapping.column()]);
|
|
@@ -89,7 +89,8 @@ export class Sankey extends SVGWidget {
|
|
|
89
89
|
__id: id,
|
|
90
90
|
__category: mapping.column(),
|
|
91
91
|
name: row.key,
|
|
92
|
-
origRow: row.
|
|
92
|
+
origRow: row.value,
|
|
93
|
+
value: row.value[idx][valueIdx]
|
|
93
94
|
});
|
|
94
95
|
vertexIndex[id] = retVal.vertices.length - 1;
|
|
95
96
|
}
|
|
@@ -107,7 +108,7 @@ export class Sankey extends SVGWidget {
|
|
|
107
108
|
__id: sourceID + "_" + targetID,
|
|
108
109
|
source: vertexIndex[sourceID],
|
|
109
110
|
target: vertexIndex[targetID],
|
|
110
|
-
value:
|
|
111
|
+
value: value.value[0][valueIdx]
|
|
111
112
|
});
|
|
112
113
|
});
|
|
113
114
|
});
|
|
@@ -116,7 +117,6 @@ export class Sankey extends SVGWidget {
|
|
|
116
117
|
|
|
117
118
|
return retVal;
|
|
118
119
|
}
|
|
119
|
-
|
|
120
120
|
enter(domNode, element) {
|
|
121
121
|
super.enter(domNode, element);
|
|
122
122
|
|
|
@@ -129,14 +129,22 @@ export class Sankey extends SVGWidget {
|
|
|
129
129
|
|
|
130
130
|
this._palette = this._palette.switch(this.paletteID());
|
|
131
131
|
|
|
132
|
+
const strokeWidth = this.vertexStrokeWidth();
|
|
133
|
+
|
|
132
134
|
const sankeyData = this.sankeyData();
|
|
135
|
+
const sw2 = strokeWidth * 2;
|
|
133
136
|
this._d3Sankey
|
|
134
|
-
.extent([
|
|
137
|
+
.extent([
|
|
138
|
+
[strokeWidth, strokeWidth],
|
|
139
|
+
[this.width() - sw2, this.height() - sw2]
|
|
140
|
+
])
|
|
135
141
|
.nodeWidth(this.vertexWidth())
|
|
136
142
|
.nodePadding(this.vertexPadding())
|
|
137
143
|
;
|
|
138
|
-
|
|
139
|
-
|
|
144
|
+
this._d3Sankey({
|
|
145
|
+
nodes: sankeyData.vertices,
|
|
146
|
+
links: sankeyData.edges
|
|
147
|
+
});
|
|
140
148
|
const context = this;
|
|
141
149
|
|
|
142
150
|
// Links ---
|
|
@@ -150,15 +158,16 @@ export class Sankey extends SVGWidget {
|
|
|
150
158
|
})
|
|
151
159
|
.merge(link)
|
|
152
160
|
.attr("d", d3SankeyLinkHorizontal())
|
|
153
|
-
.style("stroke-width", function (d) {
|
|
154
|
-
|
|
161
|
+
.style("stroke-width", function (d) {
|
|
162
|
+
return Math.max(1, d.width);
|
|
163
|
+
})
|
|
164
|
+
.sort(function (a, b) { return b.width - a.width; })
|
|
155
165
|
.select("title")
|
|
156
166
|
.text(function (d) {
|
|
157
167
|
return d.source.name + " → " + d.target.name + "\n" + d.value;
|
|
158
168
|
})
|
|
159
169
|
;
|
|
160
170
|
link.exit().remove();
|
|
161
|
-
|
|
162
171
|
// Nodes ---
|
|
163
172
|
const node = element.selectAll(".node").data(sankeyData.vertices);
|
|
164
173
|
node.enter().append("g")
|
|
@@ -186,24 +195,32 @@ export class Sankey extends SVGWidget {
|
|
|
186
195
|
*/
|
|
187
196
|
.merge(node)
|
|
188
197
|
.attr("transform", function (d) {
|
|
189
|
-
|
|
198
|
+
let _x = 0;
|
|
199
|
+
let _y = 0;
|
|
200
|
+
if(d.x0)_x=d.x0;
|
|
201
|
+
if(d.y0)_y=d.y0;
|
|
202
|
+
return "translate(" + (_x+strokeWidth) + "," + (_y+strokeWidth) + ")";
|
|
190
203
|
})
|
|
191
204
|
.each(function () {
|
|
192
205
|
const n = d3Select(this);
|
|
193
206
|
n.select("rect")
|
|
194
|
-
.attr("height", function (d: any) { return d.
|
|
207
|
+
.attr("height", function (d: any) { return d.y1 - d.y0; })
|
|
195
208
|
.attr("width", context._d3Sankey.nodeWidth())
|
|
196
209
|
.style("fill", function (d: any) { return context._palette(d.name); })
|
|
210
|
+
.style("stroke", function (d: any) { return context.vertexStrokeColor(); })
|
|
211
|
+
.style("stroke-width", function (d: any) { return strokeWidth; })
|
|
197
212
|
.style("cursor", (context.xAxisMovement() || context.yAxisMovement()) ? null : "default")
|
|
198
213
|
;
|
|
199
214
|
n.select("text")
|
|
200
215
|
.attr("x", -6)
|
|
201
|
-
.attr("y", function (d: any) {
|
|
216
|
+
.attr("y", function (d: any) {
|
|
217
|
+
return (d.y1 - d.y0)/2;
|
|
218
|
+
})
|
|
202
219
|
.attr("dy", ".35em")
|
|
203
220
|
.attr("text-anchor", "end")
|
|
204
221
|
.attr("transform", null)
|
|
205
222
|
.text(function (d: any) { return d.name; })
|
|
206
|
-
.filter(function (d: any) { return d.
|
|
223
|
+
.filter(function (d: any) { return d.x0 < context.width() / 2; })
|
|
207
224
|
.attr("x", 6 + context._d3Sankey.nodeWidth())
|
|
208
225
|
.attr("text-anchor", "start")
|
|
209
226
|
;
|
|
@@ -240,6 +257,8 @@ export class Sankey extends SVGWidget {
|
|
|
240
257
|
|
|
241
258
|
paletteID: { (): string; (_: string): Sankey; };
|
|
242
259
|
mappings: { (): SankeyColumn[]; (_: SankeyColumn[]): Sankey; };
|
|
260
|
+
vertexStrokeWidth: { (): number; (_: number): Sankey; };
|
|
261
|
+
vertexStrokeColor: { (): string; (_: string): Sankey; };
|
|
243
262
|
vertexWidth: { (): number; (_: number): Sankey; };
|
|
244
263
|
vertexPadding: { (): number; (_: number): Sankey; };
|
|
245
264
|
xAxisMovement: { (): boolean; (_: boolean): Sankey; };
|
|
@@ -266,6 +285,8 @@ Sankey.prototype._palette = Palette.ordinal("default");
|
|
|
266
285
|
|
|
267
286
|
Sankey.prototype.publish("paletteID", "default", "set", "Color palette for this widget", Sankey.prototype._palette.switch());
|
|
268
287
|
Sankey.prototype.publish("mappings", [], "propertyArray", "Source Columns", null, { autoExpand: SankeyColumn });
|
|
288
|
+
Sankey.prototype.publish("vertexStrokeWidth", 1, "number", "Vertex Stroke Width");
|
|
289
|
+
Sankey.prototype.publish("vertexStrokeColor", "darkgray", "string", "Vertex Stroke Color");
|
|
269
290
|
Sankey.prototype.publish("vertexWidth", 36, "number", "Vertex Width");
|
|
270
291
|
Sankey.prototype.publish("vertexPadding", 40, "number", "Vertex Padding");
|
|
271
292
|
Sankey.prototype.publish("xAxisMovement", false, "boolean", "Enable x-axis movement");
|
package/src/__package__.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export const PKG_NAME = "@hpcc-js/graph";
|
|
2
|
-
export const PKG_VERSION = "2.
|
|
3
|
-
export const BUILD_VERSION = "2.
|
|
2
|
+
export const PKG_VERSION = "2.74.0";
|
|
3
|
+
export const BUILD_VERSION = "2.95.0";
|
package/src/graph2/dataGraph.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { compare2 } from "@hpcc-js/util";
|
|
|
4
4
|
import { Graph2 } from "./graph";
|
|
5
5
|
import { IEdge, IHierarchy, ISubgraph, IVertex } from "./layouts/placeholders";
|
|
6
6
|
|
|
7
|
-
function toJsonObj(row, columns) {
|
|
7
|
+
export function toJsonObj(row, columns) {
|
|
8
8
|
const retVal = {};
|
|
9
9
|
columns.forEach((c, i) => retVal[c] = row[i]);
|
|
10
10
|
return retVal;
|
|
@@ -76,8 +76,10 @@ export class DataGraph extends Graph2 {
|
|
|
76
76
|
edgeSourceColumn: publish<this, string>;
|
|
77
77
|
@publish("", "set", "Edge target ID column", function (this: DataGraph) { return this.edgeColumns(); }, { optional: true })
|
|
78
78
|
edgeTargetColumn: publish<this, string>;
|
|
79
|
-
@publish("", "set", "Edge
|
|
79
|
+
@publish("", "set", "Edge weight column", function (this: DataGraph) { return this.edgeColumns(); }, { optional: true })
|
|
80
80
|
edgeWeightColumn: publish<this, string>;
|
|
81
|
+
@publish("", "set", "Edge color column", function (this: DataGraph) { return this.edgeColumns(); }, { optional: true })
|
|
82
|
+
edgeColorColumn: publish<this, string>;
|
|
81
83
|
|
|
82
84
|
@publish([], "any", "Subgraph Columns")
|
|
83
85
|
hierarchyColumns: publish<this, string[]>;
|
|
@@ -124,26 +126,31 @@ export class DataGraph extends Graph2 {
|
|
|
124
126
|
return retVal >= 0 ? retVal : columns.indexOf(defColumn);
|
|
125
127
|
}
|
|
126
128
|
|
|
127
|
-
private _prevSubgraphs:
|
|
129
|
+
private _prevSubgraphs: readonly ISubgraph[] = [];
|
|
128
130
|
private _masterSubgraphs: ISubgraph[] = [];
|
|
129
131
|
private _masterSubgraphsMap: { [key: string]: ISubgraph } = {};
|
|
130
132
|
mergeSubgraphs() {
|
|
131
133
|
const columns = this.subgraphColumns();
|
|
132
134
|
const idIdx = this.indexOf(columns, this.subgraphIDColumn(), "id");
|
|
133
135
|
const labelIdx = this.indexOf(columns, this.subgraphLabelColumn(), "label");
|
|
134
|
-
const subgraphs = this.subgraphs()
|
|
135
|
-
|
|
136
|
+
const subgraphs = this.subgraphs().map((sg): ISubgraph => {
|
|
137
|
+
return {
|
|
138
|
+
id: "" + sg[idIdx],
|
|
139
|
+
text: "" + sg[labelIdx],
|
|
140
|
+
origData: toJsonObj(sg, columns)
|
|
141
|
+
};
|
|
142
|
+
});
|
|
143
|
+
const diff = compare2(this._prevSubgraphs, subgraphs, d => d.id);
|
|
136
144
|
diff.exit.forEach(item => {
|
|
137
|
-
this._masterSubgraphs = this._masterSubgraphs.filter(i => i.id !== item
|
|
145
|
+
this._masterSubgraphs = this._masterSubgraphs.filter(i => i.id !== item.id);
|
|
146
|
+
delete this._masterSubgraphsMap[item.id];
|
|
138
147
|
});
|
|
139
148
|
diff.enter.forEach(item => {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
this._masterSubgraphs.push(sg);
|
|
146
|
-
this._masterSubgraphsMap[sg.id] = sg;
|
|
149
|
+
this._masterSubgraphs.push(item);
|
|
150
|
+
this._masterSubgraphsMap[item.id] = item;
|
|
151
|
+
});
|
|
152
|
+
diff.update.forEach(item => {
|
|
153
|
+
this._masterSubgraphsMap[item.id].origData = item.origData;
|
|
147
154
|
});
|
|
148
155
|
this._prevSubgraphs = subgraphs;
|
|
149
156
|
}
|
|
@@ -178,16 +185,21 @@ export class DataGraph extends Graph2 {
|
|
|
178
185
|
const diff = compare2(this._prevVertices, vertices, d => d.id);
|
|
179
186
|
diff.exit.forEach(item => {
|
|
180
187
|
this._masterVertices = this._masterVertices.filter(i => i.id !== item.id);
|
|
188
|
+
delete this._masterVerticesMap[item.id];
|
|
181
189
|
});
|
|
182
190
|
diff.enter.forEach(item => {
|
|
183
191
|
this._masterVertices.push(item);
|
|
184
192
|
this._masterVerticesMap[item.id] = item;
|
|
185
193
|
});
|
|
194
|
+
diff.update.forEach(item => {
|
|
195
|
+
this._masterVerticesMap[item.id].origData = item.origData;
|
|
196
|
+
});
|
|
186
197
|
this._prevVertices = vertices;
|
|
187
198
|
}
|
|
188
199
|
|
|
189
200
|
protected _prevEdges: readonly IEdge[] = [];
|
|
190
201
|
protected _masterEdges: IEdge[] = [];
|
|
202
|
+
private _masterEdgesMap: { [key: string]: IEdge } = {};
|
|
191
203
|
mergeEdges() {
|
|
192
204
|
const columns = this.edgeColumns();
|
|
193
205
|
const idIdx = this.indexOf(columns, this.edgeIDColumn(), "id");
|
|
@@ -195,6 +207,7 @@ export class DataGraph extends Graph2 {
|
|
|
195
207
|
const targetIdx = this.indexOf(columns, this.edgeTargetColumn(), "target");
|
|
196
208
|
const labelIdx = this.indexOf(columns, this.edgeLabelColumn(), "label");
|
|
197
209
|
const weightIdx = this.indexOf(columns, this.edgeWeightColumn(), "weight");
|
|
210
|
+
const colorIdx = this.indexOf(columns, this.edgeColorColumn(), "color");
|
|
198
211
|
const edges: IEdge[] = this.edges().map(e => {
|
|
199
212
|
const source = this._masterVerticesMap["" + e[sourceIdx]];
|
|
200
213
|
if (!source) console.error(`Invalid edge source entity "${e[sourceIdx]}" does not exist.`);
|
|
@@ -206,6 +219,7 @@ export class DataGraph extends Graph2 {
|
|
|
206
219
|
source,
|
|
207
220
|
target,
|
|
208
221
|
weight: +e[weightIdx] || 1,
|
|
222
|
+
color: e[colorIdx] as string,
|
|
209
223
|
label: labelIdx >= 0 ? ("" + e[labelIdx]) : "",
|
|
210
224
|
origData: toJsonObj(e, columns)
|
|
211
225
|
};
|
|
@@ -213,9 +227,15 @@ export class DataGraph extends Graph2 {
|
|
|
213
227
|
const diff = compare2(this._masterEdges, edges, d => d.id);
|
|
214
228
|
diff.exit.forEach(item => {
|
|
215
229
|
this._masterEdges = this._masterEdges.filter(i => i.id !== item.id);
|
|
230
|
+
delete this._masterEdgesMap[item.id];
|
|
216
231
|
});
|
|
217
232
|
diff.enter.forEach(item => {
|
|
218
233
|
this._masterEdges.push(item);
|
|
234
|
+
this._masterEdgesMap[item.id] = item;
|
|
235
|
+
|
|
236
|
+
});
|
|
237
|
+
diff.update.forEach(item => {
|
|
238
|
+
this._masterEdgesMap[item.id].origData = item.origData;
|
|
219
239
|
});
|
|
220
240
|
this._prevEdges = edges;
|
|
221
241
|
}
|
|
@@ -237,6 +257,7 @@ export class DataGraph extends Graph2 {
|
|
|
237
257
|
const diff = compare2(this._prevHierarchy, hierarchy, d => d.id);
|
|
238
258
|
diff.exit.forEach(item => {
|
|
239
259
|
this._masterHierarchy = this._masterHierarchy.filter(i => i.id !== item.id);
|
|
260
|
+
delete this._masterHierarchyMap[item.id];
|
|
240
261
|
});
|
|
241
262
|
diff.enter.forEach(item => {
|
|
242
263
|
this._masterHierarchy.push(item);
|
package/src/graph2/graph.ts
CHANGED
|
@@ -119,10 +119,10 @@ export class Graph2 extends SVGZoomWidget {
|
|
|
119
119
|
|
|
120
120
|
const selection = context.selection();
|
|
121
121
|
const isSelected = context.selected(d.props, selection as IVertex[]);
|
|
122
|
-
if(isSelected) {
|
|
122
|
+
if (isSelected) {
|
|
123
123
|
selection
|
|
124
|
-
.filter(v=>v.id !== d.props.id)
|
|
125
|
-
.forEach(v=>{
|
|
124
|
+
.filter(v => v.id !== d.props.id)
|
|
125
|
+
.forEach(v => {
|
|
126
126
|
const n = context._graphData.vertex(v.id);
|
|
127
127
|
dragStart(n);
|
|
128
128
|
});
|
|
@@ -141,11 +141,11 @@ export class Graph2 extends SVGZoomWidget {
|
|
|
141
141
|
context.moveVertexPlaceholder(d, false, true);
|
|
142
142
|
const selection = context.selection();
|
|
143
143
|
const isSelected = context.selected(d.props, selection as IVertex[]);
|
|
144
|
-
|
|
145
|
-
if(isSelected) {
|
|
144
|
+
|
|
145
|
+
if (isSelected) {
|
|
146
146
|
selection
|
|
147
|
-
.filter(v=>v.id !== d.props.id)
|
|
148
|
-
.forEach(v=>{
|
|
147
|
+
.filter(v => v.id !== d.props.id)
|
|
148
|
+
.forEach(v => {
|
|
149
149
|
const n = context._graphData.vertex(v.id);
|
|
150
150
|
dragTick(n, d);
|
|
151
151
|
context.moveVertexPlaceholder(n, false, true);
|
|
@@ -166,20 +166,20 @@ export class Graph2 extends SVGZoomWidget {
|
|
|
166
166
|
|
|
167
167
|
const selection = context.selection();
|
|
168
168
|
const isSelected = context.selected(d.props, selection as IVertex[]);
|
|
169
|
-
if(isSelected) {
|
|
169
|
+
if (isSelected) {
|
|
170
170
|
selection
|
|
171
|
-
.filter(v=>v.id !== d.props.id)
|
|
172
|
-
.forEach(v=>{
|
|
171
|
+
.filter(v => v.id !== d.props.id)
|
|
172
|
+
.forEach(v => {
|
|
173
173
|
const n = context._graphData.vertex(v.id);
|
|
174
174
|
dragEnd(n);
|
|
175
175
|
});
|
|
176
176
|
} else if (context.dragSingleNeighbors()) {
|
|
177
177
|
context._graphData.singleNeighbors(d.id).forEach(dragEnd);
|
|
178
178
|
}
|
|
179
|
-
|
|
179
|
+
|
|
180
180
|
d3Select(this).classed("grabbed", false);
|
|
181
181
|
}
|
|
182
|
-
if(doClick) {
|
|
182
|
+
if (doClick) {
|
|
183
183
|
context._selection.click({
|
|
184
184
|
_id: d.id,
|
|
185
185
|
element: () => d.element
|
|
@@ -188,13 +188,13 @@ export class Graph2 extends SVGZoomWidget {
|
|
|
188
188
|
const selected = d.element.classed("selected");
|
|
189
189
|
context.vertex_click(d.props.origData || d.props, "", selected);
|
|
190
190
|
const doClickTime = Date.now();
|
|
191
|
-
if(doClickTime - context._prevDoClickTime < context.doubleClickMaxDelay()){
|
|
191
|
+
if (doClickTime - context._prevDoClickTime < context.doubleClickMaxDelay()) {
|
|
192
192
|
context.vertex_dblclick(d.props.origData || d.props, "", selected);
|
|
193
193
|
}
|
|
194
194
|
context._prevDoClickTime = doClickTime;
|
|
195
195
|
}
|
|
196
196
|
})
|
|
197
|
-
.filter(()=>true)
|
|
197
|
+
.filter(() => true)
|
|
198
198
|
;
|
|
199
199
|
this.zoomToFitLimit(1);
|
|
200
200
|
}
|
|
@@ -270,7 +270,7 @@ export class Graph2 extends SVGZoomWidget {
|
|
|
270
270
|
}
|
|
271
271
|
|
|
272
272
|
selected(vertex: IVertex, _?: Array<IVertex>): boolean {
|
|
273
|
-
return (_ || this.selection()).some(n=>n.id === vertex.id);
|
|
273
|
+
return (_ || this.selection()).some(n => n.id === vertex.id);
|
|
274
274
|
}
|
|
275
275
|
|
|
276
276
|
selection(_: Array<IVertex | ISubgraph | IEdge>): this;
|
|
@@ -293,6 +293,7 @@ export class Graph2 extends SVGZoomWidget {
|
|
|
293
293
|
|
|
294
294
|
resetLayout() {
|
|
295
295
|
delete this._prevLayout;
|
|
296
|
+
return this;
|
|
296
297
|
}
|
|
297
298
|
|
|
298
299
|
layoutRunning() {
|
|
@@ -534,11 +535,11 @@ export class Graph2 extends SVGZoomWidget {
|
|
|
534
535
|
if (sp.element) {
|
|
535
536
|
// TODO: any should not be needed (tsc issue?)
|
|
536
537
|
(transition ? sp.element.transition() as any : sp.element)
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
538
|
+
.attr("transform", `translate(${x} ${y})`)
|
|
539
|
+
.each(function (d) {
|
|
540
|
+
render(Subgraph, { ...d.props, width, height }, this);
|
|
541
|
+
})
|
|
542
|
+
;
|
|
542
543
|
}
|
|
543
544
|
return this;
|
|
544
545
|
}
|
|
@@ -548,9 +549,9 @@ export class Graph2 extends SVGZoomWidget {
|
|
|
548
549
|
if (ep.elementPath) {
|
|
549
550
|
// TODO: any should not be needed (tsc issue?)
|
|
550
551
|
(transition ? ep.elementPath.transition() as any : ep.elementPath)
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
552
|
+
.attr("d", edgeLayout.path)
|
|
553
|
+
.attr("stroke-dasharray", d => d.props.strokeDasharray)
|
|
554
|
+
;
|
|
554
555
|
}
|
|
555
556
|
|
|
556
557
|
if (ep.elementText) {
|
|
@@ -567,7 +568,7 @@ export class Graph2 extends SVGZoomWidget {
|
|
|
567
568
|
.text(d => d),
|
|
568
569
|
exit => exit.remove()
|
|
569
570
|
)
|
|
570
|
-
|
|
571
|
+
;
|
|
571
572
|
// TODO: any should not be needed (tsc issue?)
|
|
572
573
|
(transition ? ep.elementText.transition() as any : ep.elementText)
|
|
573
574
|
.attr("transform", `translate(${edgeLayout.labelPos[0]} ${edgeLayout.labelPos[1]})`)
|
|
@@ -700,7 +701,9 @@ export class Graph2 extends SVGZoomWidget {
|
|
|
700
701
|
})
|
|
701
702
|
.remove()
|
|
702
703
|
)
|
|
703
|
-
.style("stroke",
|
|
704
|
+
.style("stroke", d => {
|
|
705
|
+
return d.props?.color ?? this.edgeColor();
|
|
706
|
+
})
|
|
704
707
|
.style("stroke-width", this.edgeStrokeWidth() + "px")
|
|
705
708
|
;
|
|
706
709
|
return this;
|
|
@@ -808,9 +811,9 @@ export class Graph2 extends SVGZoomWidget {
|
|
|
808
811
|
return this;
|
|
809
812
|
}
|
|
810
813
|
|
|
811
|
-
calcProps(isCentroid: boolean, props){
|
|
812
|
-
if(!props.icon)props.icon={};
|
|
813
|
-
if(isCentroid) {
|
|
814
|
+
calcProps(isCentroid: boolean, props) {
|
|
815
|
+
if (!props.icon) props.icon = {};
|
|
816
|
+
if (isCentroid) {
|
|
814
817
|
props.textHeight = props.textHeight ? props.textHeight : this.centroidTextHeight() * this.centroidScale();
|
|
815
818
|
props.textPadding = props.textPadding ? props.textPadding : this.centroidTextPadding() * this.centroidScale();
|
|
816
819
|
props.textFontFamily = props.textFontFamily ? props.textFontFamily : this.centroidLabelFontFamily();
|
|
@@ -832,7 +835,7 @@ export class Graph2 extends SVGZoomWidget {
|
|
|
832
835
|
const fontSize = props.icon.height - props.icon.padding;
|
|
833
836
|
const rect = this.textRect(text, fontFamily, fontSize);
|
|
834
837
|
|
|
835
|
-
props.icon.yOffset = -(rect.top - (fontSize/2)) - (rect.height/2) + (props.icon.padding > 0 ? fontSize/props.icon.padding/2 : 0);
|
|
838
|
+
props.icon.yOffset = -(rect.top - (fontSize / 2)) - (rect.height / 2) + (props.icon.padding > 0 ? fontSize / props.icon.padding / 2 : 0);
|
|
836
839
|
return props;
|
|
837
840
|
}
|
|
838
841
|
|
|
@@ -943,7 +946,7 @@ export class Graph2 extends SVGZoomWidget {
|
|
|
943
946
|
};
|
|
944
947
|
case "Tree":
|
|
945
948
|
case "Dendrogram":
|
|
946
|
-
return { rankdir:
|
|
949
|
+
return { rankdir: this.treeRankDirection() };
|
|
947
950
|
case "DOT":
|
|
948
951
|
case "Neato":
|
|
949
952
|
case "FDP":
|
|
@@ -1254,6 +1257,9 @@ export interface Graph2 {
|
|
|
1254
1257
|
forceDirectedMaxDistance(): number;
|
|
1255
1258
|
forceDirectedMaxDistance(_: number): this;
|
|
1256
1259
|
|
|
1260
|
+
treeRankDirection(): "TB" | "LR";
|
|
1261
|
+
treeRankDirection(_: "TB" | "LR"): this;
|
|
1262
|
+
|
|
1257
1263
|
edgeColor(): string;
|
|
1258
1264
|
edgeColor(_: string): this;
|
|
1259
1265
|
edgeStrokeWidth(): number;
|
|
@@ -1336,6 +1342,8 @@ Graph2.prototype.publish("forceDirectedForceStrength", 0, "number", "Strength of
|
|
|
1336
1342
|
Graph2.prototype.publish("forceDirectedMinDistance", 1, "number", "Min distance between nodes", null, { disable: (w: Graph2) => w.layout().indexOf("ForceDirected") !== 0 });
|
|
1337
1343
|
Graph2.prototype.publish("forceDirectedMaxDistance", Infinity, "number", "Max distance between nodes", null, { disable: (w: Graph2) => w.layout().indexOf("ForceDirected") !== 0 });
|
|
1338
1344
|
|
|
1345
|
+
Graph2.prototype.publish("treeRankDirection", "LR", "set", "Direction for Rank Nodes", ["TB", "LR"], { disable: (w: Graph2) => w.layout() !== "Tree" && w.layout() !== "Dendrogram" });
|
|
1346
|
+
|
|
1339
1347
|
Graph2.prototype.publish("wasmFolder", null, "string", "WASM Folder", null, { optional: true, disable: (w: Graph2) => ["DOT", "Neato", "FDP", "TwoPI", "Circo"].indexOf(w.layout()) < 0 });
|
|
1340
1348
|
|
|
1341
1349
|
const _origScale = Graph2.prototype.scale;
|
package/src/graph2/index.ts
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
|
+
import { curveBasis as d3CurveBasis, line as d3Line } from "d3-shape";
|
|
1
2
|
import { Cluster, graphviz as gvWorker, Node } from "./graphvizWorker";
|
|
2
|
-
import { Layout } from "./layout";
|
|
3
|
+
import { Layout, Point } from "./layout";
|
|
4
|
+
import { EdgePlaceholder } from "./placeholders";
|
|
3
5
|
|
|
4
6
|
declare const window: any;
|
|
5
7
|
|
|
6
8
|
type Engine = "circo" | "dot" | "fdp" | "neato" | "osage" | "patchwork" | "twopi";
|
|
7
9
|
|
|
10
|
+
const lineBasis = d3Line<Point>()
|
|
11
|
+
.x(d => d[0])
|
|
12
|
+
.y(d => d[1])
|
|
13
|
+
.curve(d3CurveBasis)
|
|
14
|
+
;
|
|
15
|
+
|
|
8
16
|
export class Graphviz extends Layout {
|
|
9
17
|
|
|
10
18
|
_engine: Engine;
|
|
@@ -84,4 +92,28 @@ export class Graphviz extends Layout {
|
|
|
84
92
|
return this;
|
|
85
93
|
});
|
|
86
94
|
}
|
|
95
|
+
|
|
96
|
+
edgePath(ep: EdgePlaceholder, curveDepth: number): { path: string, labelPos: Point } {
|
|
97
|
+
let points = [];
|
|
98
|
+
let hasNaN = false;
|
|
99
|
+
if (ep.points) {
|
|
100
|
+
points = ep.points.map(p => {
|
|
101
|
+
const x = this._graph.project(p[0], false);
|
|
102
|
+
const y = this._graph.project(p[1], false);
|
|
103
|
+
if (isNaN(x) || isNaN(y)) {
|
|
104
|
+
hasNaN = true;
|
|
105
|
+
}
|
|
106
|
+
return [x, y];
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
if (hasNaN || points.length < 2) {
|
|
110
|
+
return super.edgePath(ep, curveDepth);
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
path: lineBasis(points),
|
|
114
|
+
labelPos: this.center(points)
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
87
118
|
}
|
|
119
|
+
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
.graph_SankeyGraph .node rect {
|
|
2
|
+
cursor: move;
|
|
3
|
+
fill-opacity: .9;
|
|
4
|
+
shape-rendering: crispEdges;
|
|
5
|
+
stroke: darkgray;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.graph_SankeyGraph .node.selected rect {
|
|
9
|
+
stroke: red;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.graph_SankeyGraph .node.over rect {
|
|
13
|
+
stroke: orange;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.graph_SankeyGraph .node.selected.over rect {
|
|
17
|
+
stroke: red;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.graph_SankeyGraph .node text {
|
|
21
|
+
pointer-events: none;
|
|
22
|
+
text-shadow: 0 1px 0 #fff;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.graph_SankeyGraph .node.selected text {
|
|
26
|
+
fill: red;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.graph_SankeyGraph .node.over text {
|
|
30
|
+
fill: orange;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.graph_SankeyGraph .node.selected.over text {
|
|
34
|
+
fill: red;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.graph_SankeyGraph .link {
|
|
38
|
+
fill: none;
|
|
39
|
+
stroke: #000;
|
|
40
|
+
stroke-opacity: .2;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.graph_SankeyGraph .link:hover {
|
|
44
|
+
stroke-opacity: .5;
|
|
45
|
+
}
|