@progress/kendo-charts 2.0.0 → 2.1.0-dev.202401181402
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/cdn/js/kendo-charts.js +1 -1
- package/dist/cdn/main.js +1 -1
- package/dist/es/{map/scroller → common}/observable.js +1 -1
- package/dist/es/main.js +1 -0
- package/dist/es/map/attribution.js +1 -1
- package/dist/es/map/map.js +1 -1
- package/dist/es/map/navigator.js +1 -1
- package/dist/es/map/scroller/draggable.js +1 -1
- package/dist/es/map/scroller/scroller.js +1 -1
- package/dist/es/map/scroller/user-events.js +1 -1
- package/dist/es/map/zoom.js +1 -1
- package/dist/es/sankey/calculation.js +176 -0
- package/dist/es/sankey/element.js +49 -0
- package/dist/es/sankey/label.js +83 -0
- package/dist/es/sankey/link.js +62 -0
- package/dist/es/sankey/node.js +50 -0
- package/dist/es/sankey/sankey.js +345 -0
- package/dist/es/sankey.js +1 -0
- package/dist/es2015/{map/scroller → common}/observable.js +1 -1
- package/dist/es2015/main.js +1 -0
- package/dist/es2015/map/attribution.js +1 -1
- package/dist/es2015/map/map.js +1 -1
- package/dist/es2015/map/navigator.js +1 -1
- package/dist/es2015/map/scroller/draggable.js +1 -1
- package/dist/es2015/map/scroller/scroller.js +1 -1
- package/dist/es2015/map/scroller/user-events.js +1 -1
- package/dist/es2015/map/zoom.js +1 -1
- package/dist/es2015/sankey/calculation.js +170 -0
- package/dist/es2015/sankey/element.js +41 -0
- package/dist/es2015/sankey/label.js +69 -0
- package/dist/es2015/sankey/link.js +49 -0
- package/dist/es2015/sankey/node.js +40 -0
- package/dist/es2015/sankey/sankey.js +315 -0
- package/dist/es2015/sankey.js +1 -0
- package/dist/npm/main.d.ts +1 -0
- package/dist/npm/main.js +743 -1
- package/dist/npm/sankey.d.ts +244 -0
- package/dist/systemjs/kendo-charts.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
import { drawing } from '@progress/kendo-drawing';
|
|
2
|
+
import { deepExtend, addClass, setDefaultOptions } from '../common';
|
|
3
|
+
import { calculateSankey } from './calculation';
|
|
4
|
+
import { Node, resolveNodeOptions } from './node';
|
|
5
|
+
import { Link, resolveLinkOptions } from './link';
|
|
6
|
+
import { Label, resolveLabelOptions } from './label';
|
|
7
|
+
import { LEFT } from '../common/constants';
|
|
8
|
+
import Box from '../core/box';
|
|
9
|
+
import rectToBox from '../core/utils/rect-to-box';
|
|
10
|
+
import { Observable } from '../common/observable';
|
|
11
|
+
|
|
12
|
+
var LINK = 'link';
|
|
13
|
+
var NODE = 'node';
|
|
14
|
+
|
|
15
|
+
export var Sankey = (function (Observable) {
|
|
16
|
+
function Sankey(element, options, theme) {
|
|
17
|
+
Observable.call(this);
|
|
18
|
+
|
|
19
|
+
this._initElement(element);
|
|
20
|
+
this._initTheme(theme);
|
|
21
|
+
this._setOptions(options);
|
|
22
|
+
this._initSurface();
|
|
23
|
+
|
|
24
|
+
if (options && options.data) {
|
|
25
|
+
this._redraw();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
this._initResizeObserver();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if ( Observable ) Sankey.__proto__ = Observable;
|
|
32
|
+
Sankey.prototype = Object.create( Observable && Observable.prototype );
|
|
33
|
+
Sankey.prototype.constructor = Sankey;
|
|
34
|
+
|
|
35
|
+
Sankey.prototype.destroy = function destroy () {
|
|
36
|
+
this.unbind();
|
|
37
|
+
this._destroySurface();
|
|
38
|
+
this._destroyResizeObserver();
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
Sankey.prototype._initElement = function _initElement (element) {
|
|
42
|
+
this.element = element;
|
|
43
|
+
addClass(this.element, [ "k-chart", "k-sankey" ]);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
Sankey.prototype._initSurface = function _initSurface () {
|
|
47
|
+
if (!this.surface) {
|
|
48
|
+
this._destroySurface();
|
|
49
|
+
this._initSurfaceElement();
|
|
50
|
+
this.surface = this._createSurface();
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
Sankey.prototype._initResizeObserver = function _initResizeObserver () {
|
|
55
|
+
var this$1 = this;
|
|
56
|
+
|
|
57
|
+
var observer = new ResizeObserver(function (entries) {
|
|
58
|
+
entries.forEach(function (entry) {
|
|
59
|
+
var ref = entry.contentRect;
|
|
60
|
+
var width = ref.width;
|
|
61
|
+
var height = ref.height;
|
|
62
|
+
if (entry.target !== this$1.element ||
|
|
63
|
+
(this$1.size.width === width && this$1.size.height === height)) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
this$1.size = { width: width, height: height };
|
|
67
|
+
this$1.surface.setSize(this$1.size);
|
|
68
|
+
this$1._redraw();
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
this._resizeObserver = observer;
|
|
72
|
+
observer.observe(this.element);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
Sankey.prototype._createSurface = function _createSurface () {
|
|
76
|
+
return drawing.Surface.create(this.surfaceElement, {
|
|
77
|
+
mouseenter: this._mouseenter.bind(this),
|
|
78
|
+
mouseleave: this._mouseleave.bind(this)
|
|
79
|
+
});
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
Sankey.prototype._initTheme = function _initTheme (theme) {
|
|
83
|
+
var currentTheme = theme || this.theme || {};
|
|
84
|
+
this.theme = currentTheme;
|
|
85
|
+
this.options = deepExtend({}, currentTheme, this.options);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
Sankey.prototype.setLinksOpacity = function setLinksOpacity (opacity) {
|
|
89
|
+
var this$1 = this;
|
|
90
|
+
|
|
91
|
+
this.linksVisuals.forEach(function (link) {
|
|
92
|
+
this$1.setOpacity(link, opacity);
|
|
93
|
+
});
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
Sankey.prototype.setOpacity = function setOpacity (link, opacity) {
|
|
97
|
+
link.options.set('stroke', Object.assign({}, link.options.stroke, {opacity: opacity}));
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
Sankey.prototype.trigger = function trigger (name, ev) {
|
|
101
|
+
ev.type = name;
|
|
102
|
+
return Observable.prototype.trigger.call(this, name, ev);
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
Sankey.prototype._mouseenter = function _mouseenter (ev) {
|
|
106
|
+
var this$1 = this;
|
|
107
|
+
|
|
108
|
+
var element = ev.element;
|
|
109
|
+
var isLink = element.type === LINK;
|
|
110
|
+
var isNode = element.type === NODE;
|
|
111
|
+
|
|
112
|
+
if ((isLink && this.trigger('linkEnter', ev)) ||
|
|
113
|
+
(isNode && this.trigger('nodeEnter', ev))) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
var ref = this.options.links;
|
|
118
|
+
var highlight = ref.highlight;
|
|
119
|
+
if (isLink) {
|
|
120
|
+
this.setLinksOpacity(highlight.inactiveOpacity);
|
|
121
|
+
this.setOpacity(element, highlight.opacity);
|
|
122
|
+
} else if (isNode) {
|
|
123
|
+
this.setLinksOpacity(highlight.inactiveOpacity);
|
|
124
|
+
element.links.forEach(function (link) {
|
|
125
|
+
this$1.setOpacity(link, highlight.opacity);
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
Sankey.prototype._mouseleave = function _mouseleave (ev) {
|
|
131
|
+
var element = ev.element;
|
|
132
|
+
var isLink = element.type === LINK;
|
|
133
|
+
var isNode = element.type === NODE;
|
|
134
|
+
var target = ev.originalEvent.relatedTarget;
|
|
135
|
+
|
|
136
|
+
if (isLink && target && target.nodeName === 'text') {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if ((isLink && this.trigger('linkLeave', ev)) ||
|
|
141
|
+
(isNode && this.trigger('nodeLeave', ev))) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (isLink || isNode) {
|
|
146
|
+
this.setLinksOpacity(this.options.links.opacity);
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
Sankey.prototype._destroySurface = function _destroySurface () {
|
|
151
|
+
if (this.surface) {
|
|
152
|
+
this.surface.destroy();
|
|
153
|
+
this.surface = null;
|
|
154
|
+
this._destroySurfaceElement();
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
Sankey.prototype._destroyResizeObserver = function _destroyResizeObserver () {
|
|
159
|
+
if (this._resizeObserver) {
|
|
160
|
+
this._resizeObserver.disconnect();
|
|
161
|
+
this._resizeObserver = null;
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
Sankey.prototype._initSurfaceElement = function _initSurfaceElement () {
|
|
166
|
+
if (!this.surfaceElement) {
|
|
167
|
+
this.surfaceElement = document.createElement('div');
|
|
168
|
+
this.element.appendChild(this.surfaceElement);
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
Sankey.prototype._destroySurfaceElement = function _destroySurfaceElement () {
|
|
173
|
+
if (this.surfaceElement && this.surfaceElement.parentNode) {
|
|
174
|
+
this.surfaceElement.parentNode.removeChild(this.surfaceElement);
|
|
175
|
+
this.surfaceElement = null;
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
Sankey.prototype.setOptions = function setOptions (options, theme) {
|
|
180
|
+
this._setOptions(options);
|
|
181
|
+
this._initTheme(theme);
|
|
182
|
+
this._initSurface();
|
|
183
|
+
this._redraw();
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
Sankey.prototype._redraw = function _redraw () {
|
|
187
|
+
this.surface.clear();
|
|
188
|
+
|
|
189
|
+
var ref = this._getSize();
|
|
190
|
+
var width = ref.width;
|
|
191
|
+
var height = ref.height;
|
|
192
|
+
this.size = { width: width, height: height };
|
|
193
|
+
this.surface.setSize(this.size);
|
|
194
|
+
|
|
195
|
+
this.createVisual();
|
|
196
|
+
|
|
197
|
+
this.surface.draw(this.visual);
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
Sankey.prototype._getSize = function _getSize () {
|
|
201
|
+
return this.element.getBoundingClientRect();
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
Sankey.prototype.createVisual = function createVisual () {
|
|
205
|
+
this.visual = this._render();
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
Sankey.prototype.calculateSankey = function calculateSankey$1 (options) {
|
|
209
|
+
var ref = this.options;
|
|
210
|
+
var nodes = ref.nodes;
|
|
211
|
+
var labels = ref.labels;
|
|
212
|
+
var nodesColors = ref.nodesColors;
|
|
213
|
+
var calculatedNodes = calculateSankey(options).nodes;
|
|
214
|
+
var box = new Box();
|
|
215
|
+
|
|
216
|
+
calculatedNodes.forEach(function (nodeEl, i) {
|
|
217
|
+
var nodeOps = resolveNodeOptions(nodeEl, nodes, nodesColors, i);
|
|
218
|
+
var nodeInstance = new Node(nodeOps);
|
|
219
|
+
box.wrap(rectToBox(nodeInstance.exportVisual().rawBBox()));
|
|
220
|
+
|
|
221
|
+
var labelInstance = new Label(deepExtend({ node: nodeEl, totalWidth: options.width }, labels));
|
|
222
|
+
var labelVisual = labelInstance.exportVisual();
|
|
223
|
+
if (labelVisual) {
|
|
224
|
+
box.wrap(rectToBox(labelVisual.rawBBox()));
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
var offsetX = box.x1 < 0 ? -box.x1 : 0;
|
|
229
|
+
var offsetY = box.y1 < 0 ? -box.y1 : 0;
|
|
230
|
+
|
|
231
|
+
var width = box.width() > options.width ? offsetX + options.width - (box.width() - options.width) : options.width;
|
|
232
|
+
var height = box.height() > options.height ? offsetY + options.height - (box.height() - options.height) : options.height;
|
|
233
|
+
|
|
234
|
+
return calculateSankey(Object.assign({}, options, {width: width, height: height, offsetX: offsetX, offsetY: offsetY}));
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
Sankey.prototype._render = function _render () {
|
|
238
|
+
var ref = this.options;
|
|
239
|
+
var data = ref.data;
|
|
240
|
+
var labelOptions = ref.labels;
|
|
241
|
+
var nodesOptions = ref.nodes;
|
|
242
|
+
var linkOptions = ref.links;
|
|
243
|
+
var nodesColors = ref.nodesColors;
|
|
244
|
+
var ref$1 = this.size;
|
|
245
|
+
var width = ref$1.width;
|
|
246
|
+
var height = ref$1.height;
|
|
247
|
+
var calcOptions = Object.assign({}, data, {width: width, height: height, nodesOptions: nodesOptions});
|
|
248
|
+
var ref$2 = this.calculateSankey(calcOptions);
|
|
249
|
+
var nodes = ref$2.nodes;
|
|
250
|
+
var links = ref$2.links;
|
|
251
|
+
|
|
252
|
+
var visual = new drawing.Group();
|
|
253
|
+
|
|
254
|
+
var visualNodes = new Map();
|
|
255
|
+
|
|
256
|
+
nodes.forEach(function (node, i) {
|
|
257
|
+
var nodeOps = resolveNodeOptions(node, nodesOptions, nodesColors, i);
|
|
258
|
+
|
|
259
|
+
var nodeInstance = new Node(nodeOps);
|
|
260
|
+
var nodeVisual = nodeInstance.exportVisual();
|
|
261
|
+
nodeVisual.links = [];
|
|
262
|
+
nodeVisual.type = NODE;
|
|
263
|
+
visualNodes.set(node.id, nodeVisual);
|
|
264
|
+
|
|
265
|
+
visual.append(nodeVisual);
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
links = links.slice().sort(function (a, b) { return b.value - a.value; });
|
|
269
|
+
|
|
270
|
+
var linksVisuals = [];
|
|
271
|
+
|
|
272
|
+
links.forEach(function (link) {
|
|
273
|
+
var source = link.source;
|
|
274
|
+
var target = link.target;
|
|
275
|
+
var sourceNode = visualNodes.get(source.id);
|
|
276
|
+
var targetNode = visualNodes.get(target.id);
|
|
277
|
+
var linkOps = resolveLinkOptions(link, linkOptions, sourceNode, targetNode);
|
|
278
|
+
var linkInstance = new Link(linkOps);
|
|
279
|
+
var linkVisual = linkInstance.exportVisual();
|
|
280
|
+
|
|
281
|
+
linkVisual.type = LINK;
|
|
282
|
+
linksVisuals.push(linkVisual);
|
|
283
|
+
|
|
284
|
+
sourceNode.links.push(linkVisual);
|
|
285
|
+
targetNode.links.push(linkVisual);
|
|
286
|
+
|
|
287
|
+
visual.append(linkVisual);
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
this.linksVisuals = linksVisuals;
|
|
291
|
+
|
|
292
|
+
nodes.forEach(function (node) {
|
|
293
|
+
var textOps = resolveLabelOptions(node, labelOptions, width);
|
|
294
|
+
var labelInstance = new Label(textOps);
|
|
295
|
+
var labelVisual = labelInstance.exportVisual();
|
|
296
|
+
|
|
297
|
+
if (labelVisual) {
|
|
298
|
+
visual.append(labelVisual);
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
return visual;
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
Sankey.prototype.exportVisual = function exportVisual () {
|
|
306
|
+
return this._render();
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
Sankey.prototype._setOptions = function _setOptions (options) {
|
|
310
|
+
this.options = deepExtend({}, this.options, options);
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
return Sankey;
|
|
314
|
+
}(Observable));
|
|
315
|
+
|
|
316
|
+
setDefaultOptions(Sankey, {
|
|
317
|
+
labels: {
|
|
318
|
+
visible: true,
|
|
319
|
+
margin: {
|
|
320
|
+
left: 8,
|
|
321
|
+
right: 8
|
|
322
|
+
},
|
|
323
|
+
padding: 0,
|
|
324
|
+
border: {
|
|
325
|
+
width: 0
|
|
326
|
+
},
|
|
327
|
+
align: LEFT,
|
|
328
|
+
opacity: 1,
|
|
329
|
+
offset: { left: 0, top: 0 }
|
|
330
|
+
},
|
|
331
|
+
nodes: {
|
|
332
|
+
width: 24,
|
|
333
|
+
padding: 16,
|
|
334
|
+
opacity: 1,
|
|
335
|
+
offset: { left: 0, top: 0 }
|
|
336
|
+
},
|
|
337
|
+
links: {
|
|
338
|
+
colorType: 'static', // 'source', 'target', 'static'
|
|
339
|
+
opacity: 0.4,
|
|
340
|
+
highlight: {
|
|
341
|
+
opacity: 0.8,
|
|
342
|
+
inactiveOpacity: 0.2
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Sankey } from './sankey/sankey';
|
package/dist/es2015/main.js
CHANGED
package/dist/es2015/map/map.js
CHANGED
package/dist/es2015/map/zoom.js
CHANGED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { deepExtend } from '../common';
|
|
2
|
+
|
|
3
|
+
const max = (array, mapFn) => Math.max.apply(null, array.map(mapFn));
|
|
4
|
+
const min = (array, mapFn) => Math.min.apply(null, array.map(mapFn));
|
|
5
|
+
const sum = (array, mapFn) => array.map(mapFn).reduce((acc, curr) => (acc + curr), 0);
|
|
6
|
+
const sortAsc = (a, b) => a.y0 - b.y0;
|
|
7
|
+
const sortSource = (a, b) => sortAsc(a.source, b.source);
|
|
8
|
+
const sortTarget = (a, b) => sortAsc(a.target, b.target);
|
|
9
|
+
const value = (node) => node.value;
|
|
10
|
+
|
|
11
|
+
function sortLinks(nodes) {
|
|
12
|
+
nodes.forEach((node) => {
|
|
13
|
+
const { sourceLinks, targetLinks } = node;
|
|
14
|
+
sourceLinks.sort(sortTarget);
|
|
15
|
+
targetLinks.sort(sortSource);
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
class Sankey {
|
|
20
|
+
constructor(options) {
|
|
21
|
+
const offset = options.nodesOptions.offset || {};
|
|
22
|
+
this.data = {
|
|
23
|
+
nodes: options.nodes.map((node) => deepExtend({}, { offset }, node)),
|
|
24
|
+
links: options.links.map((link) => deepExtend({}, link))
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
this.width = options.width;
|
|
28
|
+
this.height = options.height;
|
|
29
|
+
this.offsetX = options.offsetX || 0;
|
|
30
|
+
this.offsetY = options.offsetY || 0;
|
|
31
|
+
this.nodeWidth = options.nodesOptions.width;
|
|
32
|
+
this.nodePadding = options.nodesOptions.padding;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
calculate() {
|
|
36
|
+
const { nodes, links } = this.data;
|
|
37
|
+
this.connectLinksToNodes(nodes, links);
|
|
38
|
+
this.calculateNodeValues(nodes);
|
|
39
|
+
this.calculateNodeDepths(nodes);
|
|
40
|
+
|
|
41
|
+
const columns = this.calculateNodeColumns(nodes);
|
|
42
|
+
this.calculateNodeBreadths(columns);
|
|
43
|
+
this.applyNodesOffset(nodes);
|
|
44
|
+
this.calculateLinkBreadths(nodes);
|
|
45
|
+
|
|
46
|
+
return this.data;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
connectLinksToNodes(nodes, links) {
|
|
50
|
+
const nodesMap = new Map();
|
|
51
|
+
|
|
52
|
+
nodes.forEach((node) => {
|
|
53
|
+
node.sourceLinks = [];
|
|
54
|
+
node.targetLinks = [];
|
|
55
|
+
node.id = node.id !== undefined ? node.id : node.label.text;
|
|
56
|
+
nodesMap.set(node.id, node);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
links.forEach((link) => {
|
|
60
|
+
link.source = nodesMap.get(link.sourceId);
|
|
61
|
+
link.target = nodesMap.get(link.targetId);
|
|
62
|
+
link.source.sourceLinks.push(link);
|
|
63
|
+
link.target.targetLinks.push(link);
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
calculateNodeValues(nodes) {
|
|
68
|
+
nodes.forEach((node) => {
|
|
69
|
+
node.value = Math.max(
|
|
70
|
+
sum(node.sourceLinks, value),
|
|
71
|
+
sum(node.targetLinks, value)
|
|
72
|
+
);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
calculateNodeDepths(nodes) {
|
|
77
|
+
let current = new Set(nodes);
|
|
78
|
+
let next = new Set();
|
|
79
|
+
let currDepth = 0;
|
|
80
|
+
while (current.size) {
|
|
81
|
+
const currentNodes = Array.from(current);
|
|
82
|
+
for (let n = 0; n < currentNodes.length; n++) {
|
|
83
|
+
const node = currentNodes[n];
|
|
84
|
+
node.depth = currDepth;
|
|
85
|
+
for (let l = 0; l < node.sourceLinks.length; l++) {
|
|
86
|
+
const link = node.sourceLinks[l];
|
|
87
|
+
next.add(link.target);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
currDepth++;
|
|
91
|
+
current = next;
|
|
92
|
+
next = new Set();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
calculateNodeColumns(nodes) {
|
|
97
|
+
const maxDepth = max(nodes, (d) => d.depth);
|
|
98
|
+
const columnWidth = (this.width - this.offsetX - this.nodeWidth) / maxDepth;
|
|
99
|
+
const columns = new Array(maxDepth + 1);
|
|
100
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
101
|
+
const node = nodes[i];
|
|
102
|
+
const depth = Math.max(0, Math.min(maxDepth, node.sourceLinks.length ? node.depth : maxDepth));
|
|
103
|
+
node.x0 = this.offsetX + depth * columnWidth;
|
|
104
|
+
node.x1 = node.x0 + this.nodeWidth;
|
|
105
|
+
columns[depth] = columns[depth] || [];
|
|
106
|
+
columns[depth].push(node);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return columns;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
calculateNodeBreadths(columns) {
|
|
113
|
+
const kSize = min(columns, (c) => (this.height - this.offsetY - (c.length - 1) * this.nodePadding) / sum(c, value));
|
|
114
|
+
|
|
115
|
+
for (let c = 0; c < columns.length; c++) {
|
|
116
|
+
const nodes = columns[c];
|
|
117
|
+
let y = this.offsetY;
|
|
118
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
119
|
+
const node = nodes[i];
|
|
120
|
+
node.y0 = y;
|
|
121
|
+
node.y1 = y + node.value * kSize;
|
|
122
|
+
y = node.y1 + this.nodePadding;
|
|
123
|
+
for (let l = 0; l < node.sourceLinks.length; l++) {
|
|
124
|
+
const link = node.sourceLinks[l];
|
|
125
|
+
link.width = link.value * kSize;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
y = (this.height - y + this.nodePadding) / (nodes.length + 1);
|
|
130
|
+
for (let i = 0; i < nodes.length; ++i) {
|
|
131
|
+
const node = nodes[i];
|
|
132
|
+
node.y0 += y * (i + 1);
|
|
133
|
+
node.y1 += y * (i + 1);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
for (let c = 0; c < columns.length; c++) {
|
|
138
|
+
sortLinks(columns[c]);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
applyNodesOffset(nodes) {
|
|
143
|
+
nodes.forEach((node) => {
|
|
144
|
+
const offsetX = (node.offset ? node.offset.left : 0) || 0;
|
|
145
|
+
const offsetY = (node.offset ? node.offset.top : 0) || 0;
|
|
146
|
+
node.x0 += offsetX;
|
|
147
|
+
node.x1 += offsetX;
|
|
148
|
+
node.y0 += offsetY;
|
|
149
|
+
node.y1 += offsetY;
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
calculateLinkBreadths(nodes) {
|
|
154
|
+
nodes.forEach((node) => {
|
|
155
|
+
const { sourceLinks, targetLinks } = node;
|
|
156
|
+
let y = node.y0;
|
|
157
|
+
let y1 = y;
|
|
158
|
+
sourceLinks.forEach((link) => {
|
|
159
|
+
link.y0 = y + link.width / 2;
|
|
160
|
+
y += link.width;
|
|
161
|
+
});
|
|
162
|
+
targetLinks.forEach((link) => {
|
|
163
|
+
link.y1 = y1 + link.width / 2;
|
|
164
|
+
y1 += link.width;
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export const calculateSankey = (options) => new Sankey(options).calculate();
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Class,
|
|
3
|
+
deepExtend
|
|
4
|
+
} from '../common';
|
|
5
|
+
|
|
6
|
+
export class SankeyElement extends Class {
|
|
7
|
+
constructor(options) {
|
|
8
|
+
super();
|
|
9
|
+
this.options = deepExtend({}, this.options, options);
|
|
10
|
+
this.createVisual();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
createVisual() {
|
|
14
|
+
this.visual = this.createElement();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
exportVisual() {
|
|
18
|
+
return this.visual;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
createElement() {
|
|
22
|
+
const customVisual = this.options.visual;
|
|
23
|
+
let visual;
|
|
24
|
+
|
|
25
|
+
if (customVisual) {
|
|
26
|
+
visual = customVisual({
|
|
27
|
+
sender: this.getSender(),
|
|
28
|
+
options: this.visualOptions(),
|
|
29
|
+
createVisual: () => this.getElement()
|
|
30
|
+
});
|
|
31
|
+
} else {
|
|
32
|
+
visual = this.getElement();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return visual;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
getSender() {
|
|
39
|
+
return this;
|
|
40
|
+
}
|
|
41
|
+
}
|