@progress/kendo-charts 2.4.0-dev.202405201104 → 2.4.0-dev.202405211537
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/chart/legend/legend-item.js +1 -1
- package/dist/es/sankey/legend.js +5 -0
- package/dist/es/sankey/link.js +139 -1
- package/dist/es/sankey/node.js +56 -5
- package/dist/es/sankey/sankey.js +319 -4
- package/dist/es2015/chart/legend/legend-item.js +1 -1
- package/dist/es2015/sankey/legend.js +5 -0
- package/dist/es2015/sankey/link.js +119 -1
- package/dist/es2015/sankey/node.js +55 -5
- package/dist/es2015/sankey/sankey.js +307 -7
- package/dist/npm/main.js +518 -11
- package/dist/npm/sankey.d.ts +65 -2
- package/dist/systemjs/kendo-charts.js +1 -1
- package/package.json +1 -1
package/dist/es/sankey/sankey.js
CHANGED
|
@@ -5,7 +5,7 @@ import { Node, resolveNodeOptions } from './node';
|
|
|
5
5
|
import { Link, resolveLinkOptions } from './link';
|
|
6
6
|
import { Label, resolveLabelOptions } from './label';
|
|
7
7
|
import { Title } from './title';
|
|
8
|
-
import { BOTTOM, LEFT, RIGHT, TOP } from '../common/constants';
|
|
8
|
+
import { BLACK, BOTTOM, LEFT, RIGHT, TOP } from '../common/constants';
|
|
9
9
|
import { Box, rectToBox } from '../core';
|
|
10
10
|
import { Legend } from './legend';
|
|
11
11
|
import { defined } from '../drawing-utils';
|
|
@@ -25,6 +25,7 @@ export var Sankey = (function (Observable) {
|
|
|
25
25
|
if (options && options.data) {
|
|
26
26
|
this._redraw();
|
|
27
27
|
this._initResizeObserver();
|
|
28
|
+
this._initNavigation(element);
|
|
28
29
|
}
|
|
29
30
|
}
|
|
30
31
|
|
|
@@ -36,13 +37,24 @@ export var Sankey = (function (Observable) {
|
|
|
36
37
|
this.unbind();
|
|
37
38
|
this._destroySurface();
|
|
38
39
|
this._destroyResizeObserver();
|
|
40
|
+
|
|
41
|
+
if (this.element) {
|
|
42
|
+
this.element.removeEventListener('keydown', this._keydownHandler);
|
|
43
|
+
this.element.removeEventListener('focus', this._focusHandler);
|
|
44
|
+
this.element.removeEventListener('mousedown', this._onDownHandler);
|
|
45
|
+
this.element.removeEventListener('touchstart', this._onDownHandler);
|
|
46
|
+
this.element.removeEventListener('pointerdown', this._onDownHandler);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
this._focusState = null;
|
|
50
|
+
|
|
51
|
+
this.element = null;
|
|
39
52
|
};
|
|
40
53
|
|
|
41
54
|
Sankey.prototype._initElement = function _initElement (element) {
|
|
42
55
|
this.element = element;
|
|
43
56
|
addClass(element, [ "k-chart", "k-sankey" ]);
|
|
44
57
|
element.setAttribute('role', 'graphics-document');
|
|
45
|
-
element.tabIndex = element.getAttribute("tabindex") || 0;
|
|
46
58
|
|
|
47
59
|
var ref = this.options;
|
|
48
60
|
var title = ref.title;
|
|
@@ -64,6 +76,31 @@ export var Sankey = (function (Observable) {
|
|
|
64
76
|
}
|
|
65
77
|
};
|
|
66
78
|
|
|
79
|
+
Sankey.prototype._initNavigation = function _initNavigation (element) {
|
|
80
|
+
element.tabIndex = element.getAttribute("tabindex") || 0;
|
|
81
|
+
|
|
82
|
+
if (this.options.disableKeyboardNavigation) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
this._keydownHandler = this._keydown.bind(this);
|
|
87
|
+
this._focusHandler = this._focus.bind(this);
|
|
88
|
+
this._blurHandler = this._blur.bind(this);
|
|
89
|
+
this._onDownHandler = this._onDown.bind(this);
|
|
90
|
+
|
|
91
|
+
element.addEventListener('keydown', this._keydownHandler);
|
|
92
|
+
element.addEventListener('focus', this._focusHandler);
|
|
93
|
+
element.addEventListener('blur', this._blurHandler);
|
|
94
|
+
element.addEventListener('mousedown', this._onDownHandler);
|
|
95
|
+
element.addEventListener('touchstart', this._onDownHandler);
|
|
96
|
+
element.addEventListener('pointerdown', this._onDownHandler);
|
|
97
|
+
|
|
98
|
+
this._focusState = {
|
|
99
|
+
node: this.columns[0][0],
|
|
100
|
+
link: null
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
|
|
67
104
|
Sankey.prototype._initResizeObserver = function _initResizeObserver () {
|
|
68
105
|
var this$1 = this;
|
|
69
106
|
|
|
@@ -242,16 +279,223 @@ export var Sankey = (function (Observable) {
|
|
|
242
279
|
|
|
243
280
|
Sankey.prototype._click = function _click (ev) {
|
|
244
281
|
var element = ev.element;
|
|
282
|
+
var dataItem = element.dataItem;
|
|
245
283
|
var isLink = element.type === LINK;
|
|
246
284
|
var isNode = element.type === NODE;
|
|
285
|
+
var focusState = this._focusState || {};
|
|
247
286
|
|
|
248
287
|
if (isNode) {
|
|
288
|
+
var focusedNodeClicked = !focusState.link && this.sameNode(focusState.node, dataItem);
|
|
289
|
+
|
|
290
|
+
if (!focusedNodeClicked) {
|
|
291
|
+
this._focusState = { node: dataItem, link: null };
|
|
292
|
+
this._focusNode({ highlight: false });
|
|
293
|
+
}
|
|
294
|
+
|
|
249
295
|
this.trigger('nodeClick', ev);
|
|
250
296
|
} else if (isLink) {
|
|
297
|
+
var link = {
|
|
298
|
+
sourceId: dataItem.source.id,
|
|
299
|
+
targetId: dataItem.target.id,
|
|
300
|
+
value: dataItem.value
|
|
301
|
+
};
|
|
302
|
+
var focusedLinkClicked = this.sameLink(focusState.link, link);
|
|
303
|
+
|
|
304
|
+
if (!focusedLinkClicked) {
|
|
305
|
+
this._focusState = { node: dataItem.source, link: link };
|
|
306
|
+
this._focusLink({ highlight: false });
|
|
307
|
+
}
|
|
308
|
+
|
|
251
309
|
this.trigger('linkClick', ev);
|
|
252
310
|
}
|
|
253
311
|
};
|
|
254
312
|
|
|
313
|
+
Sankey.prototype.sameNode = function sameNode (node1, node2) {
|
|
314
|
+
return node1 && node2 && node1.id === node2.id;
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
Sankey.prototype.sameLink = function sameLink (link1, link2) {
|
|
318
|
+
return link1 && link2 && link1.sourceId === link2.sourceId && link1.targetId === link2.targetId;
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
Sankey.prototype._focusNode = function _focusNode (options) {
|
|
322
|
+
this._cleanFocusHighlight();
|
|
323
|
+
|
|
324
|
+
var nodeData = this._focusState.node;
|
|
325
|
+
var node = this.models.map.get(nodeData.id);
|
|
326
|
+
node.focus(options);
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
Sankey.prototype._focusLink = function _focusLink (options) {
|
|
330
|
+
this._cleanFocusHighlight();
|
|
331
|
+
|
|
332
|
+
var linkData = this._focusState.link;
|
|
333
|
+
var link = this.models.map.get(((linkData.sourceId) + "-" + (linkData.targetId)));
|
|
334
|
+
link.focus(options);
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
Sankey.prototype._focusNextNode = function _focusNextNode (direction) {
|
|
338
|
+
if ( direction === void 0 ) direction = 1;
|
|
339
|
+
|
|
340
|
+
var current = this._focusState.node;
|
|
341
|
+
|
|
342
|
+
var columnIndex = this.columns.findIndex(function (column) { return column.find(function (n) { return n.id === current.id; }); });
|
|
343
|
+
var columnNodes = this.columns[columnIndex];
|
|
344
|
+
var nodeIndex = columnNodes.findIndex(function (n) { return n.id === current.id; });
|
|
345
|
+
|
|
346
|
+
var nextNode = columnNodes[nodeIndex + direction];
|
|
347
|
+
if (nextNode) {
|
|
348
|
+
this._focusState.node = nextNode;
|
|
349
|
+
this._focusNode();
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
Sankey.prototype._focusNextLink = function _focusNextLink (direction) {
|
|
354
|
+
if ( direction === void 0 ) direction = 1;
|
|
355
|
+
|
|
356
|
+
var node = this._focusState.node;
|
|
357
|
+
var link = this._focusState.link;
|
|
358
|
+
|
|
359
|
+
var sourceLinkIndex = node.sourceLinks.findIndex(function (l) { return l.sourceId === link.sourceId && l.targetId === link.targetId; });
|
|
360
|
+
var targetLinkIndex = node.targetLinks.findIndex(function (l) { return l.sourceId === link.sourceId && l.targetId === link.targetId; });
|
|
361
|
+
|
|
362
|
+
if (sourceLinkIndex !== -1) {
|
|
363
|
+
var nextLink = node.sourceLinks[sourceLinkIndex + direction];
|
|
364
|
+
|
|
365
|
+
if (nextLink) {
|
|
366
|
+
this._focusState.link = nextLink;
|
|
367
|
+
this._focusLink();
|
|
368
|
+
}
|
|
369
|
+
} else if (targetLinkIndex !== -1) {
|
|
370
|
+
var nextLink$1 = node.targetLinks[targetLinkIndex + direction];
|
|
371
|
+
|
|
372
|
+
if (nextLink$1) {
|
|
373
|
+
this._focusState.link = nextLink$1;
|
|
374
|
+
this._focusLink();
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
Sankey.prototype._focusSourceNode = function _focusSourceNode () {
|
|
380
|
+
var linkData = this._focusState.link;
|
|
381
|
+
var sourceNode = this.models.map.get(linkData.sourceId);
|
|
382
|
+
this._focusState = { node: sourceNode.options.node, link: null };
|
|
383
|
+
this._focusNode();
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
Sankey.prototype._focusTargetNode = function _focusTargetNode () {
|
|
387
|
+
var linkData = this._focusState.link;
|
|
388
|
+
var targetNode = this.models.map.get(linkData.targetId);
|
|
389
|
+
this._focusState = { node: targetNode.options.node, link: null };
|
|
390
|
+
this._focusNode();
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
Sankey.prototype._focusSourceLink = function _focusSourceLink () {
|
|
394
|
+
var nodeData = this._focusState.node;
|
|
395
|
+
var sourceLinks = nodeData.sourceLinks;
|
|
396
|
+
var linkData = sourceLinks[0];
|
|
397
|
+
if (linkData) {
|
|
398
|
+
this._focusState.link = linkData;
|
|
399
|
+
this._focusLink();
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
Sankey.prototype._focusTargetLink = function _focusTargetLink () {
|
|
404
|
+
var nodeData = this._focusState.node;
|
|
405
|
+
var targetLinks = nodeData.targetLinks;
|
|
406
|
+
var linkData = targetLinks[0];
|
|
407
|
+
if (linkData) {
|
|
408
|
+
this._focusState.link = linkData;
|
|
409
|
+
this._focusLink();
|
|
410
|
+
}
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
Sankey.prototype._focus = function _focus () {
|
|
414
|
+
if (!this._skipFocusHighlight) {
|
|
415
|
+
if (this._focusState.link) {
|
|
416
|
+
this._focusLink();
|
|
417
|
+
} else {
|
|
418
|
+
this._focusNode();
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
this._skipFocusHighlight = false;
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
Sankey.prototype._blur = function _blur () {
|
|
426
|
+
this._cleanFocusHighlight();
|
|
427
|
+
};
|
|
428
|
+
|
|
429
|
+
Sankey.prototype._onDown = function _onDown () {
|
|
430
|
+
if (!this._hasFocus()) {
|
|
431
|
+
this._skipFocusHighlight = true;
|
|
432
|
+
}
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
Sankey.prototype._hasFocus = function _hasFocus () {
|
|
436
|
+
return this.element.ownerDocument.activeElement === this.element;
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
Sankey.prototype._cleanFocusHighlight = function _cleanFocusHighlight () {
|
|
440
|
+
this.models.nodes.forEach(function (node) { return node.blur(); });
|
|
441
|
+
this.models.links.forEach(function (link) { return link.blur(); });
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
Sankey.prototype._keydown = function _keydown (ev) {
|
|
445
|
+
var handler = this['on' + ev.key];
|
|
446
|
+
|
|
447
|
+
if (handler) {
|
|
448
|
+
handler.call(this, ev);
|
|
449
|
+
}
|
|
450
|
+
};
|
|
451
|
+
|
|
452
|
+
Sankey.prototype.onEscape = function onEscape (ev) {
|
|
453
|
+
ev.preventDefault();
|
|
454
|
+
|
|
455
|
+
this._focusState = { node: this.columns[0][0], link: null };
|
|
456
|
+
this._focusNode();
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
Sankey.prototype.onArrowDown = function onArrowDown (ev) {
|
|
460
|
+
ev.preventDefault();
|
|
461
|
+
|
|
462
|
+
if (this._focusState.link) {
|
|
463
|
+
this._focusNextLink(1);
|
|
464
|
+
} else {
|
|
465
|
+
this._focusNextNode(1);
|
|
466
|
+
}
|
|
467
|
+
};
|
|
468
|
+
|
|
469
|
+
Sankey.prototype.onArrowUp = function onArrowUp (ev) {
|
|
470
|
+
ev.preventDefault();
|
|
471
|
+
|
|
472
|
+
if (this._focusState.link) {
|
|
473
|
+
this._focusNextLink(-1);
|
|
474
|
+
} else {
|
|
475
|
+
this._focusNextNode(-1);
|
|
476
|
+
}
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
Sankey.prototype.onArrowLeft = function onArrowLeft (ev) {
|
|
480
|
+
ev.preventDefault();
|
|
481
|
+
|
|
482
|
+
if (this._focusState.link) {
|
|
483
|
+
this._focusSourceNode();
|
|
484
|
+
} else {
|
|
485
|
+
this._focusTargetLink();
|
|
486
|
+
}
|
|
487
|
+
};
|
|
488
|
+
|
|
489
|
+
Sankey.prototype.onArrowRight = function onArrowRight (ev) {
|
|
490
|
+
ev.preventDefault();
|
|
491
|
+
|
|
492
|
+
if (this._focusState.link) {
|
|
493
|
+
this._focusTargetNode();
|
|
494
|
+
} else {
|
|
495
|
+
this._focusSourceLink();
|
|
496
|
+
}
|
|
497
|
+
};
|
|
498
|
+
|
|
255
499
|
Sankey.prototype.highlightLinks = function highlightLinks (node, highlight) {
|
|
256
500
|
var this$1 = this;
|
|
257
501
|
|
|
@@ -350,9 +594,13 @@ export var Sankey = (function (Observable) {
|
|
|
350
594
|
var labels = sankeyOptions.labels;
|
|
351
595
|
var nodeColors = sankeyOptions.nodeColors;
|
|
352
596
|
var disableAutoLayout = sankeyOptions.disableAutoLayout;
|
|
597
|
+
var disableKeyboardNavigation = sankeyOptions.disableKeyboardNavigation;
|
|
353
598
|
var autoLayout = !disableAutoLayout;
|
|
599
|
+
var padding = disableKeyboardNavigation ? 0 : highlightOptions.width / 2;
|
|
354
600
|
|
|
355
601
|
var sankeyBox = new Box(0, 0, calcOptions.width, calcOptions.height);
|
|
602
|
+
sankeyBox.unpad(padding);
|
|
603
|
+
|
|
356
604
|
var titleBox = this.titleBox(title, sankeyBox);
|
|
357
605
|
|
|
358
606
|
var legendArea = sankeyBox.clone();
|
|
@@ -394,7 +642,7 @@ export var Sankey = (function (Observable) {
|
|
|
394
642
|
var circularLinks = ref.circularLinks;
|
|
395
643
|
if (circularLinks) {
|
|
396
644
|
console.warn('Circular links detected. Kendo Sankey diagram does not support circular links.');
|
|
397
|
-
return { sankey: { nodes: [], links: [], circularLinks: circularLinks }, legendBox: legendBox, titleBox: titleBox };
|
|
645
|
+
return { sankey: { nodes: [], links: [], columns: [[]], circularLinks: circularLinks }, legendBox: legendBox, titleBox: titleBox };
|
|
398
646
|
}
|
|
399
647
|
|
|
400
648
|
var box = new Box();
|
|
@@ -485,6 +733,7 @@ export var Sankey = (function (Observable) {
|
|
|
485
733
|
var nodeColors = sankeyOptions.nodeColors;
|
|
486
734
|
var title = sankeyOptions.title;
|
|
487
735
|
var legend = sankeyOptions.legend;
|
|
736
|
+
var disableKeyboardNavigation = sankeyOptions.disableKeyboardNavigation;
|
|
488
737
|
var ref = sankeyContext.size;
|
|
489
738
|
var width = ref.width;
|
|
490
739
|
var height = ref.height;
|
|
@@ -496,6 +745,13 @@ export var Sankey = (function (Observable) {
|
|
|
496
745
|
var legendBox = ref$1.legendBox;
|
|
497
746
|
var nodes = sankey.nodes;
|
|
498
747
|
var links = sankey.links;
|
|
748
|
+
var columns = sankey.columns;
|
|
749
|
+
|
|
750
|
+
sankeyContext.columns = columns.map(function (column) {
|
|
751
|
+
var newColumn = column.slice();
|
|
752
|
+
newColumn.sort(function (a, b) { return a.y0 - b.y0; });
|
|
753
|
+
return newColumn;
|
|
754
|
+
});
|
|
499
755
|
|
|
500
756
|
var visual = new drawing.Group({
|
|
501
757
|
clip: drawing.Path.fromRect(new geometry.Rect([0, 0], [width, height]))
|
|
@@ -514,8 +770,19 @@ export var Sankey = (function (Observable) {
|
|
|
514
770
|
var visualNodes = new Map();
|
|
515
771
|
sankeyContext.nodesVisuals = visualNodes;
|
|
516
772
|
|
|
773
|
+
var models = {
|
|
774
|
+
nodes: [],
|
|
775
|
+
links: [],
|
|
776
|
+
map: new Map()
|
|
777
|
+
};
|
|
778
|
+
sankeyContext.models = models;
|
|
779
|
+
|
|
780
|
+
var focusHighlights = [];
|
|
781
|
+
|
|
517
782
|
nodes.forEach(function (node, i) {
|
|
518
783
|
var nodeOps = resolveNodeOptions(node, nodesOptions, nodeColors, i);
|
|
784
|
+
nodeOps.root = function () { return sankeyContext.element; };
|
|
785
|
+
nodeOps.navigatable = disableKeyboardNavigation !== true;
|
|
519
786
|
|
|
520
787
|
var nodeInstance = new Node(nodeOps);
|
|
521
788
|
var nodeVisual = nodeInstance.exportVisual();
|
|
@@ -532,7 +799,16 @@ export var Sankey = (function (Observable) {
|
|
|
532
799
|
targetLinks: node.targetLinks.map(function (link) { return ({ sourceId: link.sourceId, targetId: link.targetId, value: link.value }); })});
|
|
533
800
|
visualNodes.set(node.id, nodeVisual);
|
|
534
801
|
|
|
802
|
+
models.nodes.push(nodeInstance);
|
|
803
|
+
models.map.set(node.id, nodeInstance);
|
|
804
|
+
|
|
535
805
|
visual.append(nodeVisual);
|
|
806
|
+
|
|
807
|
+
nodeInstance.createFocusHighlight();
|
|
808
|
+
|
|
809
|
+
if (nodeInstance._highlight) {
|
|
810
|
+
focusHighlights.push(nodeInstance._highlight);
|
|
811
|
+
}
|
|
536
812
|
});
|
|
537
813
|
|
|
538
814
|
var sortedLinks = links.slice().sort(function (a, b) { return b.value - a.value; });
|
|
@@ -546,6 +822,8 @@ export var Sankey = (function (Observable) {
|
|
|
546
822
|
var sourceNode = visualNodes.get(source.id);
|
|
547
823
|
var targetNode = visualNodes.get(target.id);
|
|
548
824
|
var linkOps = resolveLinkOptions(link, linkOptions, sourceNode, targetNode);
|
|
825
|
+
linkOps.root = function () { return sankeyContext.element; };
|
|
826
|
+
linkOps.navigatable = disableKeyboardNavigation !== true;
|
|
549
827
|
var linkInstance = new Link(linkOps);
|
|
550
828
|
var linkVisual = linkInstance.exportVisual();
|
|
551
829
|
|
|
@@ -561,6 +839,15 @@ export var Sankey = (function (Observable) {
|
|
|
561
839
|
sourceNode.links.push(linkVisual);
|
|
562
840
|
targetNode.links.push(linkVisual);
|
|
563
841
|
|
|
842
|
+
models.links.push(linkInstance);
|
|
843
|
+
models.map.set(((source.id) + "-" + (target.id)), linkInstance);
|
|
844
|
+
|
|
845
|
+
linkInstance.createFocusHighlight();
|
|
846
|
+
|
|
847
|
+
if (linkInstance._highlight) {
|
|
848
|
+
focusHighlights.push(linkInstance._highlight);
|
|
849
|
+
}
|
|
850
|
+
|
|
564
851
|
visual.append(linkVisual);
|
|
565
852
|
});
|
|
566
853
|
|
|
@@ -581,6 +868,12 @@ export var Sankey = (function (Observable) {
|
|
|
581
868
|
visual.append(legendVisual);
|
|
582
869
|
}
|
|
583
870
|
|
|
871
|
+
if (focusHighlights.length !== 0) {
|
|
872
|
+
var focusHighlight = new drawing.Group();
|
|
873
|
+
focusHighlight.append.apply(focusHighlight, focusHighlights);
|
|
874
|
+
visual.append(focusHighlight);
|
|
875
|
+
}
|
|
876
|
+
|
|
584
877
|
return visual;
|
|
585
878
|
};
|
|
586
879
|
|
|
@@ -605,6 +898,12 @@ export var Sankey = (function (Observable) {
|
|
|
605
898
|
return Sankey;
|
|
606
899
|
}(Observable));
|
|
607
900
|
|
|
901
|
+
var highlightOptions = {
|
|
902
|
+
opacity: 1,
|
|
903
|
+
width: 2,
|
|
904
|
+
color: BLACK
|
|
905
|
+
};
|
|
906
|
+
|
|
608
907
|
setDefaultOptions(Sankey, {
|
|
609
908
|
title: {
|
|
610
909
|
position: TOP, // 'top', 'bottom'
|
|
@@ -632,7 +931,15 @@ setDefaultOptions(Sankey, {
|
|
|
632
931
|
padding: 16,
|
|
633
932
|
opacity: 1,
|
|
634
933
|
align: 'stretch', // 'left', 'right', 'stretch'
|
|
635
|
-
offset: { left: 0, top: 0 }
|
|
934
|
+
offset: { left: 0, top: 0 },
|
|
935
|
+
focusHighlight: Object.assign({}, highlightOptions),
|
|
936
|
+
labels: {
|
|
937
|
+
ariaTemplate: function (ref) {
|
|
938
|
+
var node = ref.node;
|
|
939
|
+
|
|
940
|
+
return node.label.text;
|
|
941
|
+
}
|
|
942
|
+
}
|
|
636
943
|
},
|
|
637
944
|
links: {
|
|
638
945
|
colorType: 'static', // 'source', 'target', 'static'
|
|
@@ -640,6 +947,14 @@ setDefaultOptions(Sankey, {
|
|
|
640
947
|
highlight: {
|
|
641
948
|
opacity: 0.8,
|
|
642
949
|
inactiveOpacity: 0.2
|
|
950
|
+
},
|
|
951
|
+
focusHighlight: Object.assign({}, highlightOptions),
|
|
952
|
+
labels: {
|
|
953
|
+
ariaTemplate: function (ref) {
|
|
954
|
+
var link = ref.link;
|
|
955
|
+
|
|
956
|
+
return ((link.source.label.text) + " to " + (link.target.label.text));
|
|
957
|
+
}
|
|
643
958
|
}
|
|
644
959
|
},
|
|
645
960
|
tooltip: {
|
|
@@ -243,7 +243,7 @@ class LegendItem extends BoxElement {
|
|
|
243
243
|
|
|
244
244
|
if (this.options.visible) {
|
|
245
245
|
const accessibilityOptions = deepExtend({
|
|
246
|
-
ariaLabel: options.text
|
|
246
|
+
ariaLabel: options.accessibility.ariaLabel !== undefined ? options.accessibility.ariaLabel : options.text
|
|
247
247
|
}, options.accessibility);
|
|
248
248
|
|
|
249
249
|
addAccessibilityAttributesToVisual(this.visual, accessibilityOptions);
|
|
@@ -2,6 +2,66 @@ import { drawing } from '@progress/kendo-drawing';
|
|
|
2
2
|
import { SankeyElement } from './element';
|
|
3
3
|
import { deepExtend } from '../common';
|
|
4
4
|
import { defined } from '../drawing-utils';
|
|
5
|
+
import { ARIA_ACTIVE_DESCENDANT } from '../common/constants';
|
|
6
|
+
|
|
7
|
+
const distanceToLine = (line, point) => {
|
|
8
|
+
const [x1, y1] = line[0];
|
|
9
|
+
const [x2, y2] = line[1];
|
|
10
|
+
const [x3, y3] = point;
|
|
11
|
+
|
|
12
|
+
return Math.abs((x2 - x1) * (y3 - y1) - (x3 - x1) * (y2 - y1)) / Math.sqrt(Math.pow( (x2 - x1), 2 ) + Math.pow( (y2 - y1), 2 ));
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const bezierPoint = (p1, p2, p3, p4, t) => {
|
|
16
|
+
const t1 = 1 - t;
|
|
17
|
+
const t1t1 = t1 * t1;
|
|
18
|
+
const tt = t * t;
|
|
19
|
+
return (p1 * t1t1 * t1) + (3 * p2 * t * t1t1) + (3 * p3 * tt * t1) + (p4 * tt * t);
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const angelBetweenTwoLines = (line1, line2) => {
|
|
23
|
+
const [x1, y1] = line1[0];
|
|
24
|
+
const [x2, y2] = line1[1];
|
|
25
|
+
const [x3, y3] = line2[0];
|
|
26
|
+
const [x4, y4] = line2[1];
|
|
27
|
+
|
|
28
|
+
const a1 = Math.atan2(y2 - y1, x2 - x1);
|
|
29
|
+
const a2 = Math.atan2(y4 - y3, x4 - x3);
|
|
30
|
+
|
|
31
|
+
return Math.abs(a1 - a2);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const calculateControlPointsOffsetX = (link) => {
|
|
35
|
+
const { x0, x1, y0, y1 } = link;
|
|
36
|
+
let xC = (x0 + x1) / 2;
|
|
37
|
+
|
|
38
|
+
const width = link.width;
|
|
39
|
+
const halfWidth = width / 2;
|
|
40
|
+
|
|
41
|
+
// upper curve, t = 0.5
|
|
42
|
+
const upperCurveMiddleLine = [[(x0 + xC) / 2, y0 - halfWidth], [(x1 + xC) / 2, y1 - halfWidth]];
|
|
43
|
+
|
|
44
|
+
// for lower curve, bezier-point at t = 0.5
|
|
45
|
+
// for the case t = 0.5, the bezier-point is the middle point of the curve. => ((y0 + halfWidth) + (y1 + halfWidth)) / 2
|
|
46
|
+
const lowerCurveMiddlePoint = [xC, bezierPoint(y0 + halfWidth, y0 + halfWidth, y1 + halfWidth, y1 + halfWidth, 0.5)];
|
|
47
|
+
|
|
48
|
+
// The actual width of the link at its middle point as can be seen on the screen.
|
|
49
|
+
const actualWidth = distanceToLine(upperCurveMiddleLine, lowerCurveMiddlePoint);
|
|
50
|
+
|
|
51
|
+
const upperNarrowness = (width - actualWidth) / 2;
|
|
52
|
+
|
|
53
|
+
// The line `upperCurveMiddleLine` shows the upper border of the link.
|
|
54
|
+
// Assumption 1: Translated to the left to the desired link width and the translate value will be the `offset`.
|
|
55
|
+
// Assumption 2: The translate value is a hypotenuse of a triangle.
|
|
56
|
+
const alpha = angelBetweenTwoLines(upperCurveMiddleLine, [[x0, y0 - halfWidth], [xC, y0 - halfWidth]]);
|
|
57
|
+
const a = upperNarrowness;
|
|
58
|
+
const b = Math.sin(alpha) * a;
|
|
59
|
+
const offset = Math.sqrt(a * a + b * b);
|
|
60
|
+
// Another option is to assume the triangle is isosceles
|
|
61
|
+
// => offset = Math.sqrt(2) * upperNarrowness;
|
|
62
|
+
|
|
63
|
+
return y0 - y1 > 0 ? (-1) * offset : offset;
|
|
64
|
+
};
|
|
5
65
|
|
|
6
66
|
export class Link extends SankeyElement {
|
|
7
67
|
getElement() {
|
|
@@ -13,17 +73,75 @@ export class Link extends SankeyElement {
|
|
|
13
73
|
.moveTo(x0, y0).curveTo([xC, y0], [xC, y1], [x1, y1]);
|
|
14
74
|
}
|
|
15
75
|
|
|
76
|
+
getLabelText(options) {
|
|
77
|
+
let labelTemplate = options.labels.ariaTemplate;
|
|
78
|
+
|
|
79
|
+
if (labelTemplate) {
|
|
80
|
+
return labelTemplate({ link: options.link });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
16
84
|
visualOptions() {
|
|
17
85
|
const options = this.options;
|
|
18
86
|
const link = this.options.link;
|
|
87
|
+
const ariaLabel = this.getLabelText(options);
|
|
88
|
+
|
|
19
89
|
return {
|
|
20
90
|
stroke: {
|
|
21
91
|
width: options.link.width,
|
|
22
92
|
color: link.color || options.color,
|
|
23
93
|
opacity: defined(link.opacity) ? link.opacity : options.opacity
|
|
24
|
-
}
|
|
94
|
+
},
|
|
95
|
+
role: 'graphics-symbol',
|
|
96
|
+
ariaRoleDescription: 'Link',
|
|
97
|
+
ariaLabel: ariaLabel
|
|
25
98
|
};
|
|
26
99
|
}
|
|
100
|
+
|
|
101
|
+
createFocusHighlight() {
|
|
102
|
+
if (!this.options.navigatable) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const link = this.options.link;
|
|
106
|
+
const { x0, x1, y0, y1 } = link;
|
|
107
|
+
const xC = (x0 + x1) / 2;
|
|
108
|
+
const halfWidth = link.width / 2;
|
|
109
|
+
|
|
110
|
+
const offset = calculateControlPointsOffsetX(link);
|
|
111
|
+
|
|
112
|
+
this._highlight = new drawing.Path({ stroke: this.options.focusHighlight, visible: false })
|
|
113
|
+
.moveTo(x0, y0 + halfWidth)
|
|
114
|
+
.lineTo(x0, y0 - halfWidth)
|
|
115
|
+
.curveTo([xC + offset, y0 - halfWidth], [xC + offset, y1 - halfWidth], [x1, y1 - halfWidth])
|
|
116
|
+
.lineTo(x1, y1 + halfWidth)
|
|
117
|
+
.curveTo([xC - offset, y1 + halfWidth], [xC - offset, y0 + halfWidth], [x0, y0 + halfWidth]);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
focus(options) {
|
|
121
|
+
if (this._highlight) {
|
|
122
|
+
const { highlight = true } = options || {};
|
|
123
|
+
if (highlight) {
|
|
124
|
+
this._highlight.options.set('visible', true);
|
|
125
|
+
}
|
|
126
|
+
const id = `${this.options.link.sourceId}->${this.options.link.targetId}`;
|
|
127
|
+
this.visual.options.set('id', id);
|
|
128
|
+
|
|
129
|
+
if (this.options.root()) {
|
|
130
|
+
this.options.root().setAttribute(ARIA_ACTIVE_DESCENDANT, id);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
blur() {
|
|
136
|
+
if (this._highlight) {
|
|
137
|
+
this._highlight.options.set('visible', false);
|
|
138
|
+
this.visual.options.set('id', '');
|
|
139
|
+
|
|
140
|
+
if (this.options.root()) {
|
|
141
|
+
this.options.root().removeAttribute(ARIA_ACTIVE_DESCENDANT);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
27
145
|
}
|
|
28
146
|
|
|
29
147
|
export const resolveLinkOptions = (link, options, sourceNode, targetNode) => {
|
|
@@ -1,18 +1,29 @@
|
|
|
1
1
|
import { geometry, drawing } from '@progress/kendo-drawing';
|
|
2
2
|
import { SankeyElement } from './element';
|
|
3
3
|
import { deepExtend } from '../common';
|
|
4
|
+
import { ARIA_ACTIVE_DESCENDANT } from '../common/constants';
|
|
4
5
|
|
|
5
6
|
export class Node extends SankeyElement {
|
|
6
7
|
getElement() {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
return drawing.Path.fromRect(this.getRect(), this.visualOptions());
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
getRect() {
|
|
12
|
+
const node = this.options.node;
|
|
13
|
+
return new geometry.Rect([node.x0, node.y0], [node.x1 - node.x0, node.y1 - node.y0]);
|
|
14
|
+
}
|
|
10
15
|
|
|
11
|
-
|
|
16
|
+
getLabelText(options) {
|
|
17
|
+
let labelTemplate = options.labels.ariaTemplate;
|
|
18
|
+
|
|
19
|
+
if (labelTemplate) {
|
|
20
|
+
return labelTemplate({ node: options.node });
|
|
21
|
+
}
|
|
12
22
|
}
|
|
13
23
|
|
|
14
24
|
visualOptions() {
|
|
15
25
|
const options = deepExtend({}, this.options, this.options.node);
|
|
26
|
+
const ariaLabel = this.getLabelText(options);
|
|
16
27
|
|
|
17
28
|
return {
|
|
18
29
|
fill: {
|
|
@@ -23,9 +34,48 @@ export class Node extends SankeyElement {
|
|
|
23
34
|
className: 'k-sankey-node',
|
|
24
35
|
role: 'graphics-symbol',
|
|
25
36
|
ariaRoleDescription: 'Node',
|
|
26
|
-
ariaLabel:
|
|
37
|
+
ariaLabel: ariaLabel
|
|
27
38
|
};
|
|
28
39
|
}
|
|
40
|
+
|
|
41
|
+
createFocusHighlight() {
|
|
42
|
+
if (!this.options.navigatable) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
this._highlight = new drawing.Path.fromRect(this.getRect(), {
|
|
47
|
+
stroke: this.options.focusHighlight,
|
|
48
|
+
visible: false
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
return this._highlight;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
focus(options) {
|
|
55
|
+
if (this._highlight) {
|
|
56
|
+
const { highlight = true } = options || {};
|
|
57
|
+
if (highlight) {
|
|
58
|
+
this._highlight.options.set('visible', true);
|
|
59
|
+
}
|
|
60
|
+
const id = this.options.node.id;
|
|
61
|
+
this.visual.options.set('id', id);
|
|
62
|
+
|
|
63
|
+
if (this.options.root()) {
|
|
64
|
+
this.options.root().setAttribute(ARIA_ACTIVE_DESCENDANT, id);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
blur() {
|
|
70
|
+
if (this._highlight) {
|
|
71
|
+
this._highlight.options.set('visible', false);
|
|
72
|
+
this.visual.options.set('id', '');
|
|
73
|
+
|
|
74
|
+
if (this.options.root()) {
|
|
75
|
+
this.options.root().removeAttribute(ARIA_ACTIVE_DESCENDANT);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
29
79
|
}
|
|
30
80
|
|
|
31
81
|
const nodeColor = (node, nodeColors, index) => node.color || nodeColors[index % nodeColors.length];
|