@progress/kendo-charts 2.1.1-dev.202401290907 → 2.2.0-dev.202402011056

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.
@@ -0,0 +1,59 @@
1
+ import { default as ChartLegend } from "../chart/legend/legend";
2
+ import { SankeyElement } from "./element";
3
+ import { setDefaultOptions } from '../common';
4
+ import { nodeColor } from "./node";
5
+ import { BOTTOM, CENTER, POINTER } from "../common/constants";
6
+ import { AREA } from "../chart/constants";
7
+
8
+ export var Legend = (function (SankeyElement) {
9
+ function Legend () {
10
+ SankeyElement.apply(this, arguments);
11
+ }
12
+
13
+ if ( SankeyElement ) Legend.__proto__ = SankeyElement;
14
+ Legend.prototype = Object.create( SankeyElement && SankeyElement.prototype );
15
+ Legend.prototype.constructor = Legend;
16
+
17
+ Legend.prototype.getElement = function getElement () {
18
+ var options = this.options;
19
+ var drawingRect = options.drawingRect;
20
+ var nodes = options.nodes; if ( nodes === void 0 ) nodes = [];
21
+ var colors = options.colors; if ( colors === void 0 ) colors = [];
22
+
23
+ if (options.visible === false || !nodes.length) {
24
+ return null;
25
+ }
26
+
27
+ var data = nodes.map(function (node, index) { return ({
28
+ text: (node.label && node.label.text) || '',
29
+ area: { background: nodeColor(node, colors, index), opacity: node.opacity },
30
+ node: node,
31
+ }); });
32
+
33
+ var legend = new ChartLegend(Object.assign({}, options, {data: data}));
34
+ legend.reflow(drawingRect);
35
+
36
+ legend.renderVisual();
37
+ return legend.visual;
38
+ };
39
+
40
+ Legend.prototype.createElement = function createElement () {
41
+ return this.getElement();
42
+ };
43
+
44
+ return Legend;
45
+ }(SankeyElement));
46
+
47
+ setDefaultOptions(Legend, {
48
+ markers: { visible: false },
49
+ item: {
50
+ type: AREA,
51
+ cursor: POINTER,
52
+ opacity: 1
53
+ },
54
+ position: BOTTOM,
55
+ align: CENTER,
56
+ border: {
57
+ width: 0
58
+ }
59
+ });
@@ -34,10 +34,12 @@ export var Node = (function (SankeyElement) {
34
34
  return Node;
35
35
  }(SankeyElement));
36
36
 
37
+ export var nodeColor = function (node, nodesColors, index) { return node.color || nodesColors[index % nodesColors.length]; };
38
+
37
39
  export var resolveNodeOptions = function (node, options, nodesColors, index) {
38
40
  var nodeOptions = deepExtend({}, options, options.node);
39
41
  return deepExtend({},
40
- { color: nodesColors[index % nodesColors.length] },
42
+ { color: nodeColor(node, nodesColors, index) },
41
43
  nodeOptions,
42
44
  { node: node },
43
45
  {
@@ -4,10 +4,12 @@ import { calculateSankey } from './calculation';
4
4
  import { Node, resolveNodeOptions } from './node';
5
5
  import { Link, resolveLinkOptions } from './link';
6
6
  import { Label, resolveLabelOptions } from './label';
7
- import { LEFT } from '../common/constants';
7
+ import { Title } from './title';
8
+ import { BOTTOM, LEFT, RIGHT, TOP } from '../common/constants';
8
9
  import Box from '../core/box';
9
10
  import rectToBox from '../core/utils/rect-to-box';
10
11
  import { Observable } from '../common/observable';
12
+ import { Legend } from './legend';
11
13
 
12
14
  var LINK = 'link';
13
15
  var NODE = 'node';
@@ -103,11 +105,10 @@ export var Sankey = (function (Observable) {
103
105
  };
104
106
 
105
107
  Sankey.prototype._mouseenter = function _mouseenter (ev) {
106
- var this$1 = this;
107
-
108
108
  var element = ev.element;
109
109
  var isLink = element.type === LINK;
110
110
  var isNode = element.type === NODE;
111
+ var isLegendItem = Boolean(element.chartElement && element.chartElement.options.node);
111
112
 
112
113
  if ((isLink && this.trigger('linkEnter', ev)) ||
113
114
  (isNode && this.trigger('nodeEnter', ev))) {
@@ -120,10 +121,10 @@ export var Sankey = (function (Observable) {
120
121
  this.setLinksOpacity(highlight.inactiveOpacity);
121
122
  this.setOpacity(element, highlight.opacity);
122
123
  } else if (isNode) {
123
- this.setLinksOpacity(highlight.inactiveOpacity);
124
- element.links.forEach(function (link) {
125
- this$1.setOpacity(link, highlight.opacity);
126
- });
124
+ this.highlightLinks(element, highlight);
125
+ } else if (isLegendItem) {
126
+ var nodeVisual = this.nodesVisuals.get(element.chartElement.options.node.id);
127
+ this.highlightLinks(nodeVisual, highlight);
127
128
  }
128
129
  };
129
130
 
@@ -131,6 +132,7 @@ export var Sankey = (function (Observable) {
131
132
  var element = ev.element;
132
133
  var isLink = element.type === LINK;
133
134
  var isNode = element.type === NODE;
135
+ var isLegendItem = Boolean(element.chartElement && element.chartElement.options.node);
134
136
  var target = ev.originalEvent.relatedTarget;
135
137
 
136
138
  if (isLink && target && target.nodeName === 'text') {
@@ -142,11 +144,22 @@ export var Sankey = (function (Observable) {
142
144
  return;
143
145
  }
144
146
 
145
- if (isLink || isNode) {
147
+ if (isLink || isNode || isLegendItem) {
146
148
  this.setLinksOpacity(this.options.links.opacity);
147
149
  }
148
150
  };
149
151
 
152
+ Sankey.prototype.highlightLinks = function highlightLinks (node, highlight) {
153
+ var this$1 = this;
154
+
155
+ if (node) {
156
+ this.setLinksOpacity(highlight.inactiveOpacity);
157
+ node.links.forEach(function (link) {
158
+ this$1.setOpacity(link, highlight.opacity);
159
+ });
160
+ }
161
+ };
162
+
150
163
  Sankey.prototype._destroySurface = function _destroySurface () {
151
164
  if (this.surface) {
152
165
  this.surface.destroy();
@@ -205,12 +218,74 @@ export var Sankey = (function (Observable) {
205
218
  this.visual = this._render();
206
219
  };
207
220
 
221
+ Sankey.prototype.titleBox = function titleBox (title, drawingRect) {
222
+ if (!title || title.visible === false || !title.text) {
223
+ return null;
224
+ }
225
+
226
+ var titleElement = new Title(Object.assign({}, {drawingRect: drawingRect}, title));
227
+ var titleVisual = titleElement.exportVisual();
228
+ return titleVisual.chartElement.box;
229
+ };
230
+
231
+ Sankey.prototype.legendBox = function legendBox (options, nodes, drawingRect) {
232
+ if (!options || options.visible === false) {
233
+ return null;
234
+ }
235
+
236
+ var legend = new Legend(Object.assign({}, {nodes: nodes}, options, {drawingRect: drawingRect}));
237
+ var legendVisual = legend.exportVisual();
238
+
239
+ return legendVisual.chartElement.box;
240
+ };
241
+
208
242
  Sankey.prototype.calculateSankey = function calculateSankey$1 (options) {
209
243
  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;
244
+ var title = ref.title;
245
+ var legend = ref.legend;
246
+ var data = ref.data;
247
+ var ref$1 = this.options;
248
+ var nodes = ref$1.nodes;
249
+ var labels = ref$1.labels;
250
+ var nodesColors = ref$1.nodesColors;
251
+
252
+ var sankeyBox = new Box(0, 0, options.width, options.height);
253
+ var titleBox = this.titleBox(title, sankeyBox);
254
+
255
+ var legendArea = sankeyBox.clone();
256
+
257
+ if (titleBox) {
258
+ var titleHeight = titleBox.height();
259
+ if (title.position === TOP) {
260
+ sankeyBox.unpad({ top: titleHeight });
261
+ legendArea = new Box(0, titleHeight, options.width, options.height);
262
+ } else {
263
+ sankeyBox.shrink(0, titleHeight);
264
+ legendArea = new Box(0, 0, options.width, options.height - titleHeight);
265
+ }
266
+ }
267
+
268
+ var legendBox = this.legendBox(legend, data.nodes, legendArea);
269
+
270
+ if (legendBox) {
271
+ if (legend.position === LEFT) {
272
+ sankeyBox.unpad({ left: legendBox.width() });
273
+ }
274
+
275
+ if (legend.position === RIGHT) {
276
+ sankeyBox.shrink(legendBox.width(), 0);
277
+ }
278
+
279
+ if (legend.position === TOP) {
280
+ sankeyBox.unpad({ top: legendBox.height() });
281
+ }
282
+
283
+ if (legend.position === BOTTOM) {
284
+ sankeyBox.shrink(0, legendBox.height());
285
+ }
286
+ }
287
+
288
+ var calculatedNodes = calculateSankey(Object.assign({}, options, {offsetX: sankeyBox.x1, offsetY: sankeyBox.y1, width: sankeyBox.x2, height: sankeyBox.y2})).nodes;
214
289
  var box = new Box();
215
290
 
216
291
  calculatedNodes.forEach(function (nodeEl, i) {
@@ -225,13 +300,17 @@ export var Sankey = (function (Observable) {
225
300
  }
226
301
  });
227
302
 
228
- var offsetX = box.x1 < 0 ? -box.x1 : 0;
229
- var offsetY = box.y1 < 0 ? -box.y1 : 0;
303
+ var offsetX = (box.x1 < 0 ? -box.x1 : 0) + sankeyBox.x1;
304
+ var offsetY = (box.y1 < 0 ? -box.y1 : 0) + sankeyBox.y1;
230
305
 
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;
306
+ var width = box.width() > sankeyBox.x2 ? offsetX + sankeyBox.x2 - (box.width() - sankeyBox.x2) : sankeyBox.x2;
307
+ var height = box.height() > sankeyBox.y2 ? offsetY + sankeyBox.y2 - (box.height() - sankeyBox.y2) : sankeyBox.y2;
233
308
 
234
- return calculateSankey(Object.assign({}, options, {width: width, height: height, offsetX: offsetX, offsetY: offsetY}));
309
+ return {
310
+ sankey: calculateSankey(Object.assign({}, options, {offsetX: offsetX, offsetY: offsetY, width: width, height: height})),
311
+ legendBox: legendBox,
312
+ titleBox: titleBox
313
+ };
235
314
  };
236
315
 
237
316
  Sankey.prototype._render = function _render () {
@@ -241,16 +320,33 @@ export var Sankey = (function (Observable) {
241
320
  var nodesOptions = ref.nodes;
242
321
  var linkOptions = ref.links;
243
322
  var nodesColors = ref.nodesColors;
323
+ var title = ref.title;
324
+ var legend = ref.legend;
244
325
  var ref$1 = this.size;
245
326
  var width = ref$1.width;
246
327
  var height = ref$1.height;
247
- var calcOptions = Object.assign({}, data, {width: width, height: height, nodesOptions: nodesOptions});
328
+ var calcOptions = Object.assign({}, data, {width: width, height: height, nodesOptions: nodesOptions, title: title, legend: legend});
248
329
  var ref$2 = this.calculateSankey(calcOptions);
249
- var nodes = ref$2.nodes;
250
- var links = ref$2.links;
330
+ var sankey = ref$2.sankey;
331
+ var titleBox = ref$2.titleBox;
332
+ var legendBox = ref$2.legendBox;
333
+ var nodes = sankey.nodes;
334
+ var links = sankey.links;
251
335
 
252
336
  var visual = new drawing.Group();
253
337
 
338
+ if (titleBox) {
339
+ var titleElement = new Title(Object.assign({}, title, {drawingRect: titleBox}));
340
+ var titleVisual = titleElement.exportVisual();
341
+ visual.append(titleVisual);
342
+ }
343
+
344
+ if (legendBox) {
345
+ var legendElement = new Legend(Object.assign({}, legend, {drawingRect: legendBox, nodes: nodes, colors: nodesColors}));
346
+ var legendVisual = legendElement.exportVisual();
347
+ visual.append(legendVisual);
348
+ }
349
+
254
350
  var visualNodes = new Map();
255
351
 
256
352
  nodes.forEach(function (node, i) {
@@ -265,11 +361,11 @@ export var Sankey = (function (Observable) {
265
361
  visual.append(nodeVisual);
266
362
  });
267
363
 
268
- links = links.slice().sort(function (a, b) { return b.value - a.value; });
364
+ var sortedLinks = links.slice().sort(function (a, b) { return b.value - a.value; });
269
365
 
270
366
  var linksVisuals = [];
271
367
 
272
- links.forEach(function (link) {
368
+ sortedLinks.forEach(function (link) {
273
369
  var source = link.source;
274
370
  var target = link.target;
275
371
  var sourceNode = visualNodes.get(source.id);
@@ -288,6 +384,7 @@ export var Sankey = (function (Observable) {
288
384
  });
289
385
 
290
386
  this.linksVisuals = linksVisuals;
387
+ this.nodesVisuals = visualNodes;
291
388
 
292
389
  nodes.forEach(function (node) {
293
390
  var textOps = resolveLabelOptions(node, labelOptions, width);
@@ -0,0 +1,48 @@
1
+ import { Title as ChartTitle } from "../core";
2
+ import { SankeyElement } from "./element";
3
+ import { setDefaultOptions, getSpacing } from '../common';
4
+ import { CENTER, TOP } from "../common/constants";
5
+
6
+ export var Title = (function (SankeyElement) {
7
+ function Title () {
8
+ SankeyElement.apply(this, arguments);
9
+ }
10
+
11
+ if ( SankeyElement ) Title.__proto__ = SankeyElement;
12
+ Title.prototype = Object.create( SankeyElement && SankeyElement.prototype );
13
+ Title.prototype.constructor = Title;
14
+
15
+ Title.prototype.getElement = function getElement () {
16
+ var options = this.options;
17
+ var drawingRect = options.drawingRect;
18
+ var text = options.text;
19
+
20
+ if (options.visible === false || !text) {
21
+ return null;
22
+ }
23
+
24
+ var title = ChartTitle.buildTitle(text, options);
25
+
26
+ title.reflow(drawingRect);
27
+
28
+ title.renderVisual();
29
+ return title.visual;
30
+ };
31
+
32
+ Title.prototype.createElement = function createElement () {
33
+ return this.getElement();
34
+ };
35
+
36
+ return Title;
37
+ }(SankeyElement));
38
+
39
+ setDefaultOptions(Title, {
40
+ position: TOP, // 'top', 'bottom'
41
+ align: CENTER, // 'left', 'right', 'center'
42
+ opacity: 1,
43
+ border: {
44
+ width: 0
45
+ },
46
+ margin: getSpacing(5),
47
+ padding: getSpacing(5)
48
+ });
@@ -0,0 +1,47 @@
1
+ import { default as ChartLegend } from "../chart/legend/legend";
2
+ import { SankeyElement } from "./element";
3
+ import { setDefaultOptions } from '../common';
4
+ import { nodeColor } from "./node";
5
+ import { BOTTOM, CENTER, POINTER } from "../common/constants";
6
+ import { AREA } from "../chart/constants";
7
+
8
+ export class Legend extends SankeyElement {
9
+ getElement() {
10
+ const options = this.options;
11
+ const { drawingRect, nodes = [], colors = [] } = options;
12
+
13
+ if (options.visible === false || !nodes.length) {
14
+ return null;
15
+ }
16
+
17
+ const data = nodes.map((node, index) => ({
18
+ text: (node.label && node.label.text) || '',
19
+ area: { background: nodeColor(node, colors, index), opacity: node.opacity },
20
+ node: node,
21
+ }));
22
+
23
+ const legend = new ChartLegend(Object.assign({}, options, {data}));
24
+ legend.reflow(drawingRect);
25
+
26
+ legend.renderVisual();
27
+ return legend.visual;
28
+ }
29
+
30
+ createElement() {
31
+ return this.getElement();
32
+ }
33
+ }
34
+
35
+ setDefaultOptions(Legend, {
36
+ markers: { visible: false },
37
+ item: {
38
+ type: AREA,
39
+ cursor: POINTER,
40
+ opacity: 1
41
+ },
42
+ position: BOTTOM,
43
+ align: CENTER,
44
+ border: {
45
+ width: 0
46
+ }
47
+ });
@@ -24,10 +24,12 @@ export class Node extends SankeyElement {
24
24
  }
25
25
  }
26
26
 
27
+ export const nodeColor = (node, nodesColors, index) => node.color || nodesColors[index % nodesColors.length];
28
+
27
29
  export const resolveNodeOptions = (node, options, nodesColors, index) => {
28
30
  const nodeOptions = deepExtend({}, options, options.node);
29
31
  return deepExtend({},
30
- { color: nodesColors[index % nodesColors.length] },
32
+ { color: nodeColor(node, nodesColors, index) },
31
33
  nodeOptions,
32
34
  { node },
33
35
  {
@@ -4,10 +4,12 @@ import { calculateSankey } from './calculation';
4
4
  import { Node, resolveNodeOptions } from './node';
5
5
  import { Link, resolveLinkOptions } from './link';
6
6
  import { Label, resolveLabelOptions } from './label';
7
- import { LEFT } from '../common/constants';
7
+ import { Title } from './title';
8
+ import { BOTTOM, LEFT, RIGHT, TOP } from '../common/constants';
8
9
  import Box from '../core/box';
9
10
  import rectToBox from '../core/utils/rect-to-box';
10
11
  import { Observable } from '../common/observable';
12
+ import { Legend } from './legend';
11
13
 
12
14
  const LINK = 'link';
13
15
  const NODE = 'node';
@@ -96,6 +98,7 @@ export class Sankey extends Observable {
96
98
  const element = ev.element;
97
99
  const isLink = element.type === LINK;
98
100
  const isNode = element.type === NODE;
101
+ const isLegendItem = Boolean(element.chartElement && element.chartElement.options.node);
99
102
 
100
103
  if ((isLink && this.trigger('linkEnter', ev)) ||
101
104
  (isNode && this.trigger('nodeEnter', ev))) {
@@ -107,10 +110,10 @@ export class Sankey extends Observable {
107
110
  this.setLinksOpacity(highlight.inactiveOpacity);
108
111
  this.setOpacity(element, highlight.opacity);
109
112
  } else if (isNode) {
110
- this.setLinksOpacity(highlight.inactiveOpacity);
111
- element.links.forEach(link => {
112
- this.setOpacity(link, highlight.opacity);
113
- });
113
+ this.highlightLinks(element, highlight);
114
+ } else if (isLegendItem) {
115
+ const nodeVisual = this.nodesVisuals.get(element.chartElement.options.node.id);
116
+ this.highlightLinks(nodeVisual, highlight);
114
117
  }
115
118
  }
116
119
 
@@ -118,6 +121,7 @@ export class Sankey extends Observable {
118
121
  const element = ev.element;
119
122
  const isLink = element.type === LINK;
120
123
  const isNode = element.type === NODE;
124
+ const isLegendItem = Boolean(element.chartElement && element.chartElement.options.node);
121
125
  const target = ev.originalEvent.relatedTarget;
122
126
 
123
127
  if (isLink && target && target.nodeName === 'text') {
@@ -129,11 +133,20 @@ export class Sankey extends Observable {
129
133
  return;
130
134
  }
131
135
 
132
- if (isLink || isNode) {
136
+ if (isLink || isNode || isLegendItem) {
133
137
  this.setLinksOpacity(this.options.links.opacity);
134
138
  }
135
139
  }
136
140
 
141
+ highlightLinks(node, highlight) {
142
+ if (node) {
143
+ this.setLinksOpacity(highlight.inactiveOpacity);
144
+ node.links.forEach(link => {
145
+ this.setOpacity(link, highlight.opacity);
146
+ });
147
+ }
148
+ }
149
+
137
150
  _destroySurface() {
138
151
  if (this.surface) {
139
152
  this.surface.destroy();
@@ -190,9 +203,68 @@ export class Sankey extends Observable {
190
203
  this.visual = this._render();
191
204
  }
192
205
 
206
+ titleBox(title, drawingRect) {
207
+ if (!title || title.visible === false || !title.text) {
208
+ return null;
209
+ }
210
+
211
+ const titleElement = new Title(Object.assign({}, {drawingRect}, title));
212
+ const titleVisual = titleElement.exportVisual();
213
+ return titleVisual.chartElement.box;
214
+ }
215
+
216
+ legendBox(options, nodes, drawingRect) {
217
+ if (!options || options.visible === false) {
218
+ return null;
219
+ }
220
+
221
+ const legend = new Legend(Object.assign({}, {nodes}, options, {drawingRect}));
222
+ const legendVisual = legend.exportVisual();
223
+
224
+ return legendVisual.chartElement.box;
225
+ }
226
+
193
227
  calculateSankey(options) {
228
+ const { title, legend, data } = this.options;
194
229
  const { nodes, labels, nodesColors } = this.options;
195
- const calculatedNodes = calculateSankey(options).nodes;
230
+
231
+ const sankeyBox = new Box(0, 0, options.width, options.height);
232
+ const titleBox = this.titleBox(title, sankeyBox);
233
+
234
+ let legendArea = sankeyBox.clone();
235
+
236
+ if (titleBox) {
237
+ const titleHeight = titleBox.height();
238
+ if (title.position === TOP) {
239
+ sankeyBox.unpad({ top: titleHeight });
240
+ legendArea = new Box(0, titleHeight, options.width, options.height);
241
+ } else {
242
+ sankeyBox.shrink(0, titleHeight);
243
+ legendArea = new Box(0, 0, options.width, options.height - titleHeight);
244
+ }
245
+ }
246
+
247
+ const legendBox = this.legendBox(legend, data.nodes, legendArea);
248
+
249
+ if (legendBox) {
250
+ if (legend.position === LEFT) {
251
+ sankeyBox.unpad({ left: legendBox.width() });
252
+ }
253
+
254
+ if (legend.position === RIGHT) {
255
+ sankeyBox.shrink(legendBox.width(), 0);
256
+ }
257
+
258
+ if (legend.position === TOP) {
259
+ sankeyBox.unpad({ top: legendBox.height() });
260
+ }
261
+
262
+ if (legend.position === BOTTOM) {
263
+ sankeyBox.shrink(0, legendBox.height());
264
+ }
265
+ }
266
+
267
+ const calculatedNodes = calculateSankey(Object.assign({}, options, {offsetX: sankeyBox.x1, offsetY: sankeyBox.y1, width: sankeyBox.x2, height: sankeyBox.y2})).nodes;
196
268
  const box = new Box();
197
269
 
198
270
  calculatedNodes.forEach((nodeEl, i) => {
@@ -207,23 +279,40 @@ export class Sankey extends Observable {
207
279
  }
208
280
  });
209
281
 
210
- const offsetX = box.x1 < 0 ? -box.x1 : 0;
211
- const offsetY = box.y1 < 0 ? -box.y1 : 0;
282
+ let offsetX = (box.x1 < 0 ? -box.x1 : 0) + sankeyBox.x1;
283
+ let offsetY = (box.y1 < 0 ? -box.y1 : 0) + sankeyBox.y1;
212
284
 
213
- const width = box.width() > options.width ? offsetX + options.width - (box.width() - options.width) : options.width;
214
- const height = box.height() > options.height ? offsetY + options.height - (box.height() - options.height) : options.height;
285
+ let width = box.width() > sankeyBox.x2 ? offsetX + sankeyBox.x2 - (box.width() - sankeyBox.x2) : sankeyBox.x2;
286
+ let height = box.height() > sankeyBox.y2 ? offsetY + sankeyBox.y2 - (box.height() - sankeyBox.y2) : sankeyBox.y2;
215
287
 
216
- return calculateSankey(Object.assign({}, options, {width, height, offsetX, offsetY}));
288
+ return {
289
+ sankey: calculateSankey(Object.assign({}, options, {offsetX, offsetY, width, height})),
290
+ legendBox,
291
+ titleBox
292
+ };
217
293
  }
218
294
 
219
295
  _render() {
220
- const { data, labels: labelOptions, nodes: nodesOptions, links: linkOptions, nodesColors } = this.options;
296
+ const { data, labels: labelOptions, nodes: nodesOptions, links: linkOptions, nodesColors, title, legend } = this.options;
221
297
  const { width, height } = this.size;
222
- const calcOptions = Object.assign({}, data, {width, height, nodesOptions});
223
- let { nodes, links } = this.calculateSankey(calcOptions);
298
+ const calcOptions = Object.assign({}, data, {width, height, nodesOptions, title, legend});
299
+ const { sankey, titleBox, legendBox } = this.calculateSankey(calcOptions);
300
+ const { nodes, links } = sankey;
224
301
 
225
302
  const visual = new drawing.Group();
226
303
 
304
+ if (titleBox) {
305
+ const titleElement = new Title(Object.assign({}, title, {drawingRect: titleBox}));
306
+ const titleVisual = titleElement.exportVisual();
307
+ visual.append(titleVisual);
308
+ }
309
+
310
+ if (legendBox) {
311
+ const legendElement = new Legend(Object.assign({}, legend, {drawingRect: legendBox, nodes, colors: nodesColors}));
312
+ const legendVisual = legendElement.exportVisual();
313
+ visual.append(legendVisual);
314
+ }
315
+
227
316
  const visualNodes = new Map();
228
317
 
229
318
  nodes.forEach((node, i) => {
@@ -238,11 +327,11 @@ export class Sankey extends Observable {
238
327
  visual.append(nodeVisual);
239
328
  });
240
329
 
241
- links = links.slice().sort((a, b) => b.value - a.value);
330
+ const sortedLinks = links.slice().sort((a, b) => b.value - a.value);
242
331
 
243
332
  const linksVisuals = [];
244
333
 
245
- links.forEach(link => {
334
+ sortedLinks.forEach(link => {
246
335
  const { source, target } = link;
247
336
  const sourceNode = visualNodes.get(source.id);
248
337
  const targetNode = visualNodes.get(target.id);
@@ -260,6 +349,7 @@ export class Sankey extends Observable {
260
349
  });
261
350
 
262
351
  this.linksVisuals = linksVisuals;
352
+ this.nodesVisuals = visualNodes;
263
353
 
264
354
  nodes.forEach((node) => {
265
355
  const textOps = resolveLabelOptions(node, labelOptions, width);
@@ -0,0 +1,37 @@
1
+ import { Title as ChartTitle } from "../core";
2
+ import { SankeyElement } from "./element";
3
+ import { setDefaultOptions, getSpacing } from '../common';
4
+ import { CENTER, TOP } from "../common/constants";
5
+
6
+ export class Title extends SankeyElement {
7
+ getElement() {
8
+ const options = this.options;
9
+ const { drawingRect, text } = options;
10
+
11
+ if (options.visible === false || !text) {
12
+ return null;
13
+ }
14
+
15
+ const title = ChartTitle.buildTitle(text, options);
16
+
17
+ title.reflow(drawingRect);
18
+
19
+ title.renderVisual();
20
+ return title.visual;
21
+ }
22
+
23
+ createElement() {
24
+ return this.getElement();
25
+ }
26
+ }
27
+
28
+ setDefaultOptions(Title, {
29
+ position: TOP, // 'top', 'bottom'
30
+ align: CENTER, // 'left', 'right', 'center'
31
+ opacity: 1,
32
+ border: {
33
+ width: 0
34
+ },
35
+ margin: getSpacing(5),
36
+ padding: getSpacing(5)
37
+ });