@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.
Files changed (43) hide show
  1. package/dist/index.es6.js +424 -26
  2. package/dist/index.es6.js.map +1 -1
  3. package/dist/index.js +425 -25
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.min.js +1 -1
  6. package/dist/index.min.js.map +1 -1
  7. package/package.json +8 -7
  8. package/src/Sankey.ts +35 -14
  9. package/src/__package__.ts +2 -2
  10. package/src/graph2/dataGraph.ts +34 -13
  11. package/src/graph2/graph.ts +38 -30
  12. package/src/graph2/index.ts +1 -0
  13. package/src/graph2/layouts/graphviz.ts +33 -1
  14. package/src/graph2/layouts/placeholders.ts +1 -0
  15. package/src/graph2/sankeyGraph.css +45 -0
  16. package/src/graph2/sankeyGraph.ts +322 -0
  17. package/src/test.ts +49 -4
  18. package/types/Sankey.d.ts +8 -0
  19. package/types/Sankey.d.ts.map +1 -1
  20. package/types/__package__.d.ts +2 -2
  21. package/types/graph2/dataGraph.d.ts +3 -0
  22. package/types/graph2/dataGraph.d.ts.map +1 -1
  23. package/types/graph2/graph.d.ts +3 -1
  24. package/types/graph2/graph.d.ts.map +1 -1
  25. package/types/graph2/index.d.ts +1 -0
  26. package/types/graph2/index.d.ts.map +1 -1
  27. package/types/graph2/layouts/graphviz.d.ts +6 -1
  28. package/types/graph2/layouts/graphviz.d.ts.map +1 -1
  29. package/types/graph2/layouts/placeholders.d.ts +1 -0
  30. package/types/graph2/layouts/placeholders.d.ts.map +1 -1
  31. package/types/graph2/sankeyGraph.d.ts +73 -0
  32. package/types/graph2/sankeyGraph.d.ts.map +1 -0
  33. package/types/test.d.ts +6 -1
  34. package/types/test.d.ts.map +1 -1
  35. package/types-3.4/Sankey.d.ts +8 -0
  36. package/types-3.4/__package__.d.ts +2 -2
  37. package/types-3.4/graph2/dataGraph.d.ts +3 -0
  38. package/types-3.4/graph2/graph.d.ts +3 -1
  39. package/types-3.4/graph2/index.d.ts +1 -0
  40. package/types-3.4/graph2/layouts/graphviz.d.ts +6 -1
  41. package/types-3.4/graph2/layouts/placeholders.d.ts +1 -0
  42. package/types-3.4/graph2/sankeyGraph.d.ts +73 -0
  43. 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.70.0",
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.53",
55
- "@hpcc-js/common": "^2.58.0",
56
- "@hpcc-js/html": "^2.33.0",
57
- "@hpcc-js/react": "^2.41.0",
58
- "@hpcc-js/util": "^2.39.0"
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": "57aaf182adc54cbf5c002b061a6669db7e98a981"
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.values
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: mapping2.aggregate(value.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([[0, 0], [this.width(), this.height()]])
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
- this._d3Sankey({ nodes: sankeyData.vertices, links: sankeyData.edges });
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) { return Math.max(1, d.dy); })
154
- .sort(function (a, b) { return b.dy - a.dy; })
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
- return "translate(" + d.x + "," + d.y + ")";
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.dy; })
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) { return d.dy / 2; })
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.x < context.width() / 2; })
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");
@@ -1,3 +1,3 @@
1
1
  export const PKG_NAME = "@hpcc-js/graph";
2
- export const PKG_VERSION = "2.70.0";
3
- export const BUILD_VERSION = "2.88.0";
2
+ export const PKG_VERSION = "2.74.0";
3
+ export const BUILD_VERSION = "2.95.0";
@@ -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 target ID column", function (this: DataGraph) { return this.edgeColumns(); }, { optional: true })
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: Array<Array<string | number | boolean>> = [];
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
- const diff = compare2(this._prevSubgraphs, subgraphs, d => d[idIdx] as string);
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[idIdx]);
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
- const sg: ISubgraph = {
141
- id: "" + item[idIdx],
142
- text: "" + item[labelIdx],
143
- origData: toJsonObj(item, columns)
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);
@@ -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
- .attr("transform", `translate(${x} ${y})`)
538
- .each(function (d) {
539
- render(Subgraph, { ...d.props, width, height }, this);
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
- .attr("d", edgeLayout.path)
552
- .attr("stroke-dasharray", d => d.props.strokeDasharray)
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", this.edgeColor())
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: "LR" };
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;
@@ -1,2 +1,3 @@
1
1
  export * from "./graph";
2
2
  export * from "./dataGraph";
3
+ export * from "./sankeyGraph";
@@ -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
+
@@ -19,6 +19,7 @@ export interface IEdge extends Edge {
19
19
  source: IVertex;
20
20
  target: IVertex;
21
21
  label?: string;
22
+ color?: string;
22
23
  fontFamily?: string;
23
24
  origData?: any;
24
25
  }
@@ -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
+ }