chartjs-chart-sankey 0.16.3 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chartjs-chart-sankey.cjs +104 -27
- package/dist/chartjs-chart-sankey.esm.js +104 -27
- package/dist/chartjs-chart-sankey.min.js +2 -2
- package/dist/controller.d.cts +2 -1
- package/dist/controller.d.ts +2 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.esm.d.cts +1 -1
- package/dist/index.esm.d.ts +1 -1
- package/dist/labels.d.cts +2 -3
- package/dist/labels.d.ts +2 -3
- package/dist/lib/layout.d.ts +10 -5
- package/dist/types.d.cts +13 -6
- package/dist/types.d.ts +13 -6
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* chartjs-chart-sankey v0.
|
|
2
|
+
* chartjs-chart-sankey v0.18.0
|
|
3
3
|
* https://chartjs-chart-sankey.pages.dev/
|
|
4
4
|
* (c) 2026 Jukka Kurkela
|
|
5
5
|
* Released under the MIT license
|
|
@@ -43,7 +43,7 @@ function isPatternOrGradient(value) {
|
|
|
43
43
|
const type = Object.prototype.toString.call(value);
|
|
44
44
|
return type === '[object CanvasPattern]' || type === '[object CanvasGradient]';
|
|
45
45
|
}
|
|
46
|
-
function
|
|
46
|
+
function resolveNodeOption(option, node) {
|
|
47
47
|
if (typeof option === 'function') {
|
|
48
48
|
return option(node);
|
|
49
49
|
}
|
|
@@ -442,7 +442,64 @@ const nodeByXYSize = (a, b)=>{
|
|
|
442
442
|
if (nodeY$1(a) === nodeY$1(b)) return a.size - b.size;
|
|
443
443
|
return nodeY$1(a) - nodeY$1(b);
|
|
444
444
|
};
|
|
445
|
-
|
|
445
|
+
function createColumnGapState() {
|
|
446
|
+
return {
|
|
447
|
+
lastAfter: 0,
|
|
448
|
+
realCount: 0,
|
|
449
|
+
realCumOffset: 0,
|
|
450
|
+
yHistory: []
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
function countCrossColumnPaddings(grid, colIdx, y, ownPaddings) {
|
|
454
|
+
let paddings = ownPaddings;
|
|
455
|
+
for(let col = 0; col < colIdx; col++){
|
|
456
|
+
const otherHistory = grid[col].yHistory;
|
|
457
|
+
for(let row = 0; row < otherHistory.length; row++){
|
|
458
|
+
if (otherHistory[row] > y) break;
|
|
459
|
+
paddings = Math.max(row + 1, paddings);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
return paddings;
|
|
463
|
+
}
|
|
464
|
+
function offsetForNode(state, gap, paddings, scale) {
|
|
465
|
+
const realNodesAbove = state.realCount;
|
|
466
|
+
const before = gap.before * scale;
|
|
467
|
+
const after = gap.after * scale;
|
|
468
|
+
const transitionGap = realNodesAbove > 0 ? Math.max(state.lastAfter, before) : 0;
|
|
469
|
+
const realCumOffset = state.realCumOffset + transitionGap;
|
|
470
|
+
const virtualLevels = paddings - realNodesAbove;
|
|
471
|
+
state.realCount = realNodesAbove + 1;
|
|
472
|
+
state.realCumOffset = realCumOffset;
|
|
473
|
+
state.lastAfter = after;
|
|
474
|
+
return realCumOffset + virtualLevels * before;
|
|
475
|
+
}
|
|
476
|
+
function addEvenPadding(nodeArray, gaps, scale) {
|
|
477
|
+
let maxY = 0;
|
|
478
|
+
let columnX;
|
|
479
|
+
let prev;
|
|
480
|
+
let prevGap;
|
|
481
|
+
for (const node of nodeArray){
|
|
482
|
+
const x = nodeX$1(node);
|
|
483
|
+
const gap = gaps.get(node.key) ?? {
|
|
484
|
+
after: 0,
|
|
485
|
+
before: 0
|
|
486
|
+
};
|
|
487
|
+
if (x !== columnX) {
|
|
488
|
+
columnX = x;
|
|
489
|
+
} else if (prev && prevGap) {
|
|
490
|
+
node.y = nodeY$1(prev) + prev.size + Math.max(prevGap.after, gap.before) * scale;
|
|
491
|
+
}
|
|
492
|
+
prev = node;
|
|
493
|
+
prevGap = gap;
|
|
494
|
+
maxY = Math.max(maxY, nodeY$1(node) + Math.max(node.in, node.out));
|
|
495
|
+
}
|
|
496
|
+
return maxY;
|
|
497
|
+
}
|
|
498
|
+
function addPadding(nodeArray, gaps, scale = 1, mode = 'auto') {
|
|
499
|
+
nodeArray.sort(nodeByXYSize);
|
|
500
|
+
if (mode === 'even') {
|
|
501
|
+
return addEvenPadding(nodeArray, gaps, scale);
|
|
502
|
+
}
|
|
446
503
|
let maxY = 0;
|
|
447
504
|
const columnXs = new Map();
|
|
448
505
|
const grid = [];
|
|
@@ -450,29 +507,30 @@ const nodeByXYSize = (a, b)=>{
|
|
|
450
507
|
if (!columnXs.has(x)) {
|
|
451
508
|
const index = grid.length;
|
|
452
509
|
columnXs.set(x, index);
|
|
453
|
-
grid.push(
|
|
510
|
+
grid.push(createColumnGapState());
|
|
454
511
|
return index;
|
|
455
512
|
}
|
|
456
513
|
return columnXs.get(x) ?? 0;
|
|
457
514
|
};
|
|
458
|
-
nodeArray.sort(nodeByXYSize);
|
|
459
515
|
for (const node of nodeArray){
|
|
460
516
|
const colIdx = getColIndex(nodeX$1(node));
|
|
461
|
-
const
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
517
|
+
const state = grid[colIdx];
|
|
518
|
+
const gap = gaps.get(node.key) ?? {
|
|
519
|
+
after: 0,
|
|
520
|
+
before: 0
|
|
521
|
+
};
|
|
522
|
+
const y = nodeY$1(node);
|
|
523
|
+
if (y) {
|
|
524
|
+
state.yHistory.push(y);
|
|
525
|
+
let paddings = state.yHistory.length;
|
|
465
526
|
if (node.in) {
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
for(let row = 0; row < otherColumn.length; row++){
|
|
469
|
-
if (otherColumn[row] > nodeY$1(node)) break;
|
|
470
|
-
paddings = Math.max(row + 1, paddings);
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
while(column.length < paddings)column.push(nodeY$1(node));
|
|
527
|
+
paddings = countCrossColumnPaddings(grid, colIdx, y, paddings);
|
|
528
|
+
while(state.yHistory.length < paddings)state.yHistory.push(y);
|
|
474
529
|
}
|
|
475
|
-
node.y =
|
|
530
|
+
node.y = y + offsetForNode(state, gap, paddings, scale);
|
|
531
|
+
} else {
|
|
532
|
+
state.realCount += 1;
|
|
533
|
+
state.lastAfter = gap.after * scale;
|
|
476
534
|
}
|
|
477
535
|
maxY = Math.max(maxY, nodeY$1(node) + Math.max(node.in, node.out));
|
|
478
536
|
}
|
|
@@ -505,14 +563,14 @@ function sortFlows(nodeArray) {
|
|
|
505
563
|
});
|
|
506
564
|
});
|
|
507
565
|
}
|
|
508
|
-
function layout(nodes, data, { priority, height, nodePadding, modeX }) {
|
|
566
|
+
function layout(nodes, data, { priority, height, nodePadding, nodePaddingMode, modeX }) {
|
|
509
567
|
const nodeArray = [
|
|
510
568
|
...nodes.values()
|
|
511
569
|
];
|
|
512
570
|
const maxX = calculateX(nodes, data, modeX ?? 'edge');
|
|
513
571
|
const maxY = priority ? calculateYUsingPriority(nodeArray, maxX) : calculateY(nodeArray, maxX);
|
|
514
|
-
const
|
|
515
|
-
const maxYWithPadding = addPadding(nodeArray,
|
|
572
|
+
const scale = maxY / height;
|
|
573
|
+
const maxYWithPadding = addPadding(nodeArray, nodePadding, scale, nodePaddingMode ?? 'auto');
|
|
516
574
|
sortFlows(nodeArray);
|
|
517
575
|
return {
|
|
518
576
|
maxX,
|
|
@@ -626,16 +684,29 @@ function getNodeRect(node, size, xScale, yScale, maxColumn, nodeWidth, columnPad
|
|
|
626
684
|
y
|
|
627
685
|
};
|
|
628
686
|
}
|
|
687
|
+
function resolveNodeGap(option, node) {
|
|
688
|
+
const resolved = resolveNodeOption(option ?? 10, node) ?? 10;
|
|
689
|
+
if (typeof resolved === 'number') {
|
|
690
|
+
return {
|
|
691
|
+
after: resolved,
|
|
692
|
+
before: resolved
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
return {
|
|
696
|
+
after: resolved.after ?? 10,
|
|
697
|
+
before: resolved.before ?? 10
|
|
698
|
+
};
|
|
699
|
+
}
|
|
629
700
|
function resolveNodeLabelStyle(options, node) {
|
|
630
701
|
const { backgroundColor, borderRadius = 0, color, display, font, padding = 4, position } = options.nodeLabels ?? {};
|
|
631
702
|
return {
|
|
632
|
-
backgroundColor:
|
|
703
|
+
backgroundColor: resolveNodeOption(backgroundColor, node),
|
|
633
704
|
borderRadius,
|
|
634
|
-
color:
|
|
635
|
-
display:
|
|
705
|
+
color: resolveNodeOption(color, node) ?? options.color ?? 'black',
|
|
706
|
+
display: resolveNodeOption(display, node) ?? true,
|
|
636
707
|
font,
|
|
637
708
|
padding,
|
|
638
|
-
position:
|
|
709
|
+
position: resolveNodeOption(position, node) ?? 'auto'
|
|
639
710
|
};
|
|
640
711
|
}
|
|
641
712
|
class SankeyController extends chart_js.DatasetController {
|
|
@@ -646,10 +717,15 @@ class SankeyController extends chart_js.DatasetController {
|
|
|
646
717
|
const nodes = buildNodesFromData(sankeyData, this.options);
|
|
647
718
|
const orientation = this.options.orientation ?? 'horizontal';
|
|
648
719
|
this._nodes = nodes;
|
|
720
|
+
const nodeGaps = new Map();
|
|
721
|
+
for (const node of nodes.values()){
|
|
722
|
+
nodeGaps.set(node.key, resolveNodeGap(this.options.nodePadding, node));
|
|
723
|
+
}
|
|
649
724
|
const { maxX, maxY } = layout(nodes, sankeyData, {
|
|
650
725
|
height: orientation === 'vertical' ? this.chart.width : this.chart.height,
|
|
651
726
|
modeX: this.options.modeX,
|
|
652
|
-
nodePadding:
|
|
727
|
+
nodePadding: nodeGaps,
|
|
728
|
+
nodePaddingMode: this.options.nodePaddingMode,
|
|
653
729
|
priority: !!this.options.priority
|
|
654
730
|
});
|
|
655
731
|
this._maxX = maxX;
|
|
@@ -796,7 +872,7 @@ class SankeyController extends chart_js.DatasetController {
|
|
|
796
872
|
SankeyController.id = 'sankey';
|
|
797
873
|
SankeyController.descriptors = {
|
|
798
874
|
_indexable: false,
|
|
799
|
-
_scriptable:
|
|
875
|
+
_scriptable: (name)=>name !== 'nodePadding',
|
|
800
876
|
nodeLabels: {
|
|
801
877
|
_indexable: false,
|
|
802
878
|
_scriptable: false
|
|
@@ -834,6 +910,7 @@ SankeyController.defaults = {
|
|
|
834
910
|
dataElementType: 'flow',
|
|
835
911
|
modeX: 'edge',
|
|
836
912
|
nodePadding: 10,
|
|
913
|
+
nodePaddingMode: 'auto',
|
|
837
914
|
nodeWidth: 10,
|
|
838
915
|
orientation: 'horizontal',
|
|
839
916
|
transitions: {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* chartjs-chart-sankey v0.
|
|
2
|
+
* chartjs-chart-sankey v0.18.0
|
|
3
3
|
* https://chartjs-chart-sankey.pages.dev/
|
|
4
4
|
* (c) 2026 Jukka Kurkela
|
|
5
5
|
* Released under the MIT license
|
|
@@ -40,7 +40,7 @@ function isPatternOrGradient(value) {
|
|
|
40
40
|
const type = Object.prototype.toString.call(value);
|
|
41
41
|
return type === '[object CanvasPattern]' || type === '[object CanvasGradient]';
|
|
42
42
|
}
|
|
43
|
-
function
|
|
43
|
+
function resolveNodeOption(option, node) {
|
|
44
44
|
if (typeof option === 'function') {
|
|
45
45
|
return option(node);
|
|
46
46
|
}
|
|
@@ -439,7 +439,64 @@ const nodeByXYSize = (a, b)=>{
|
|
|
439
439
|
if (nodeY$1(a) === nodeY$1(b)) return a.size - b.size;
|
|
440
440
|
return nodeY$1(a) - nodeY$1(b);
|
|
441
441
|
};
|
|
442
|
-
|
|
442
|
+
function createColumnGapState() {
|
|
443
|
+
return {
|
|
444
|
+
lastAfter: 0,
|
|
445
|
+
realCount: 0,
|
|
446
|
+
realCumOffset: 0,
|
|
447
|
+
yHistory: []
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
function countCrossColumnPaddings(grid, colIdx, y, ownPaddings) {
|
|
451
|
+
let paddings = ownPaddings;
|
|
452
|
+
for(let col = 0; col < colIdx; col++){
|
|
453
|
+
const otherHistory = grid[col].yHistory;
|
|
454
|
+
for(let row = 0; row < otherHistory.length; row++){
|
|
455
|
+
if (otherHistory[row] > y) break;
|
|
456
|
+
paddings = Math.max(row + 1, paddings);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
return paddings;
|
|
460
|
+
}
|
|
461
|
+
function offsetForNode(state, gap, paddings, scale) {
|
|
462
|
+
const realNodesAbove = state.realCount;
|
|
463
|
+
const before = gap.before * scale;
|
|
464
|
+
const after = gap.after * scale;
|
|
465
|
+
const transitionGap = realNodesAbove > 0 ? Math.max(state.lastAfter, before) : 0;
|
|
466
|
+
const realCumOffset = state.realCumOffset + transitionGap;
|
|
467
|
+
const virtualLevels = paddings - realNodesAbove;
|
|
468
|
+
state.realCount = realNodesAbove + 1;
|
|
469
|
+
state.realCumOffset = realCumOffset;
|
|
470
|
+
state.lastAfter = after;
|
|
471
|
+
return realCumOffset + virtualLevels * before;
|
|
472
|
+
}
|
|
473
|
+
function addEvenPadding(nodeArray, gaps, scale) {
|
|
474
|
+
let maxY = 0;
|
|
475
|
+
let columnX;
|
|
476
|
+
let prev;
|
|
477
|
+
let prevGap;
|
|
478
|
+
for (const node of nodeArray){
|
|
479
|
+
const x = nodeX$1(node);
|
|
480
|
+
const gap = gaps.get(node.key) ?? {
|
|
481
|
+
after: 0,
|
|
482
|
+
before: 0
|
|
483
|
+
};
|
|
484
|
+
if (x !== columnX) {
|
|
485
|
+
columnX = x;
|
|
486
|
+
} else if (prev && prevGap) {
|
|
487
|
+
node.y = nodeY$1(prev) + prev.size + Math.max(prevGap.after, gap.before) * scale;
|
|
488
|
+
}
|
|
489
|
+
prev = node;
|
|
490
|
+
prevGap = gap;
|
|
491
|
+
maxY = Math.max(maxY, nodeY$1(node) + Math.max(node.in, node.out));
|
|
492
|
+
}
|
|
493
|
+
return maxY;
|
|
494
|
+
}
|
|
495
|
+
function addPadding(nodeArray, gaps, scale = 1, mode = 'auto') {
|
|
496
|
+
nodeArray.sort(nodeByXYSize);
|
|
497
|
+
if (mode === 'even') {
|
|
498
|
+
return addEvenPadding(nodeArray, gaps, scale);
|
|
499
|
+
}
|
|
443
500
|
let maxY = 0;
|
|
444
501
|
const columnXs = new Map();
|
|
445
502
|
const grid = [];
|
|
@@ -447,29 +504,30 @@ const nodeByXYSize = (a, b)=>{
|
|
|
447
504
|
if (!columnXs.has(x)) {
|
|
448
505
|
const index = grid.length;
|
|
449
506
|
columnXs.set(x, index);
|
|
450
|
-
grid.push(
|
|
507
|
+
grid.push(createColumnGapState());
|
|
451
508
|
return index;
|
|
452
509
|
}
|
|
453
510
|
return columnXs.get(x) ?? 0;
|
|
454
511
|
};
|
|
455
|
-
nodeArray.sort(nodeByXYSize);
|
|
456
512
|
for (const node of nodeArray){
|
|
457
513
|
const colIdx = getColIndex(nodeX$1(node));
|
|
458
|
-
const
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
514
|
+
const state = grid[colIdx];
|
|
515
|
+
const gap = gaps.get(node.key) ?? {
|
|
516
|
+
after: 0,
|
|
517
|
+
before: 0
|
|
518
|
+
};
|
|
519
|
+
const y = nodeY$1(node);
|
|
520
|
+
if (y) {
|
|
521
|
+
state.yHistory.push(y);
|
|
522
|
+
let paddings = state.yHistory.length;
|
|
462
523
|
if (node.in) {
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
for(let row = 0; row < otherColumn.length; row++){
|
|
466
|
-
if (otherColumn[row] > nodeY$1(node)) break;
|
|
467
|
-
paddings = Math.max(row + 1, paddings);
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
while(column.length < paddings)column.push(nodeY$1(node));
|
|
524
|
+
paddings = countCrossColumnPaddings(grid, colIdx, y, paddings);
|
|
525
|
+
while(state.yHistory.length < paddings)state.yHistory.push(y);
|
|
471
526
|
}
|
|
472
|
-
node.y =
|
|
527
|
+
node.y = y + offsetForNode(state, gap, paddings, scale);
|
|
528
|
+
} else {
|
|
529
|
+
state.realCount += 1;
|
|
530
|
+
state.lastAfter = gap.after * scale;
|
|
473
531
|
}
|
|
474
532
|
maxY = Math.max(maxY, nodeY$1(node) + Math.max(node.in, node.out));
|
|
475
533
|
}
|
|
@@ -502,14 +560,14 @@ function sortFlows(nodeArray) {
|
|
|
502
560
|
});
|
|
503
561
|
});
|
|
504
562
|
}
|
|
505
|
-
function layout(nodes, data, { priority, height, nodePadding, modeX }) {
|
|
563
|
+
function layout(nodes, data, { priority, height, nodePadding, nodePaddingMode, modeX }) {
|
|
506
564
|
const nodeArray = [
|
|
507
565
|
...nodes.values()
|
|
508
566
|
];
|
|
509
567
|
const maxX = calculateX(nodes, data, modeX ?? 'edge');
|
|
510
568
|
const maxY = priority ? calculateYUsingPriority(nodeArray, maxX) : calculateY(nodeArray, maxX);
|
|
511
|
-
const
|
|
512
|
-
const maxYWithPadding = addPadding(nodeArray,
|
|
569
|
+
const scale = maxY / height;
|
|
570
|
+
const maxYWithPadding = addPadding(nodeArray, nodePadding, scale, nodePaddingMode ?? 'auto');
|
|
513
571
|
sortFlows(nodeArray);
|
|
514
572
|
return {
|
|
515
573
|
maxX,
|
|
@@ -623,16 +681,29 @@ function getNodeRect(node, size, xScale, yScale, maxColumn, nodeWidth, columnPad
|
|
|
623
681
|
y
|
|
624
682
|
};
|
|
625
683
|
}
|
|
684
|
+
function resolveNodeGap(option, node) {
|
|
685
|
+
const resolved = resolveNodeOption(option ?? 10, node) ?? 10;
|
|
686
|
+
if (typeof resolved === 'number') {
|
|
687
|
+
return {
|
|
688
|
+
after: resolved,
|
|
689
|
+
before: resolved
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
return {
|
|
693
|
+
after: resolved.after ?? 10,
|
|
694
|
+
before: resolved.before ?? 10
|
|
695
|
+
};
|
|
696
|
+
}
|
|
626
697
|
function resolveNodeLabelStyle(options, node) {
|
|
627
698
|
const { backgroundColor, borderRadius = 0, color, display, font, padding = 4, position } = options.nodeLabels ?? {};
|
|
628
699
|
return {
|
|
629
|
-
backgroundColor:
|
|
700
|
+
backgroundColor: resolveNodeOption(backgroundColor, node),
|
|
630
701
|
borderRadius,
|
|
631
|
-
color:
|
|
632
|
-
display:
|
|
702
|
+
color: resolveNodeOption(color, node) ?? options.color ?? 'black',
|
|
703
|
+
display: resolveNodeOption(display, node) ?? true,
|
|
633
704
|
font,
|
|
634
705
|
padding,
|
|
635
|
-
position:
|
|
706
|
+
position: resolveNodeOption(position, node) ?? 'auto'
|
|
636
707
|
};
|
|
637
708
|
}
|
|
638
709
|
class SankeyController extends DatasetController {
|
|
@@ -643,10 +714,15 @@ class SankeyController extends DatasetController {
|
|
|
643
714
|
const nodes = buildNodesFromData(sankeyData, this.options);
|
|
644
715
|
const orientation = this.options.orientation ?? 'horizontal';
|
|
645
716
|
this._nodes = nodes;
|
|
717
|
+
const nodeGaps = new Map();
|
|
718
|
+
for (const node of nodes.values()){
|
|
719
|
+
nodeGaps.set(node.key, resolveNodeGap(this.options.nodePadding, node));
|
|
720
|
+
}
|
|
646
721
|
const { maxX, maxY } = layout(nodes, sankeyData, {
|
|
647
722
|
height: orientation === 'vertical' ? this.chart.width : this.chart.height,
|
|
648
723
|
modeX: this.options.modeX,
|
|
649
|
-
nodePadding:
|
|
724
|
+
nodePadding: nodeGaps,
|
|
725
|
+
nodePaddingMode: this.options.nodePaddingMode,
|
|
650
726
|
priority: !!this.options.priority
|
|
651
727
|
});
|
|
652
728
|
this._maxX = maxX;
|
|
@@ -793,7 +869,7 @@ class SankeyController extends DatasetController {
|
|
|
793
869
|
SankeyController.id = 'sankey';
|
|
794
870
|
SankeyController.descriptors = {
|
|
795
871
|
_indexable: false,
|
|
796
|
-
_scriptable:
|
|
872
|
+
_scriptable: (name)=>name !== 'nodePadding',
|
|
797
873
|
nodeLabels: {
|
|
798
874
|
_indexable: false,
|
|
799
875
|
_scriptable: false
|
|
@@ -831,6 +907,7 @@ SankeyController.defaults = {
|
|
|
831
907
|
dataElementType: 'flow',
|
|
832
908
|
modeX: 'edge',
|
|
833
909
|
nodePadding: 10,
|
|
910
|
+
nodePaddingMode: 'auto',
|
|
834
911
|
nodeWidth: 10,
|
|
835
912
|
orientation: 'horizontal',
|
|
836
913
|
transitions: {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* chartjs-chart-sankey v0.
|
|
2
|
+
* chartjs-chart-sankey v0.18.0
|
|
3
3
|
* https://chartjs-chart-sankey.pages.dev/
|
|
4
4
|
* (c) 2026 Jukka Kurkela
|
|
5
5
|
* Released under the MIT license
|
|
6
6
|
*/
|
|
7
|
-
!function(t,o){"object"==typeof exports&&"undefined"!=typeof module?o(exports,require("chart.js"),require("chart.js/helpers")):"function"==typeof define&&define.amd?define(["exports","chart.js","chart.js/helpers"],o):o((t="undefined"!=typeof globalThis?globalThis:t||self)["chartjs-chart-sankey"]={},t.Chart,t.Chart.helpers)}(this,function(t,o,e){"use strict";const r=t=>void 0!==t;function n(t){return t&&-1!==["min","max"].indexOf(t)?t:"max"}function i(t,o){return"function"==typeof t?t(o):t&&"object"==typeof t&&!function(t){const o=Object.prototype.toString.call(t);return"[object CanvasPattern]"===o||"[object CanvasGradient]"===o}(t)?t[o.key]:t}function a(t,o,e,r,n,i,a){t.save(),t.fillStyle=o,a>0?(!function(t,o,e,r,n,i){const a=Math.max(0,Math.min(i,r/2,n/2));t.beginPath(),t.moveTo(o+a,e),t.lineTo(o+r-a,e),t.quadraticCurveTo(o+r,e,o+r,e+a),t.lineTo(o+r,e+n-a),t.quadraticCurveTo(o+r,e+n,o+r-a,e+n),t.lineTo(o+a,e+n),t.quadraticCurveTo(o,e+n,o,e+n-a),t.lineTo(o,e+a),t.quadraticCurveTo(o,e,o+a,e),t.closePath()}(t,e,r,n,i,a),t.fill()):t.fillRect(e,r,n,i),t.restore()}function s(t,o,e){const r=function(t){if(!t)return[];const o=[],e=Array.isArray(t)?t:[t];for(;e.length;){const t=e.pop();"string"==typeof t?o.unshift(...t.split("\n")):Array.isArray(t)?e.push(...t):t&&o.unshift(`${t}`)}return o}(o);if(!r.length)return;const{backgroundColor:n,borderRadius:i,color:s,font:l,lineOffset:h,padding:c}=e,d=function(t,o){return"auto"===t?o:t}(e.position,e.autoPosition),f=Number(l.lineHeight);t.font=l.string;const u=Math.max(...r.map(o=>t.measureText(o).width)),p=f*r.length,y=function(t,o,e){const{borderWidth:r,height:n,padding:i,width:a,x:s,y:l}=o,h={align:"center",x:s+a/2,y:l+n/2};return"left"===t?(h.align="right",h.x=s-r-i):"right"===t?(h.align="left",h.x=s+a+r+i):"top"===t?h.y=l-i-e/2:"bottom"===t&&(h.y=l+n+i+e/2),h}(d,e,p);t.textAlign=y.align,t.textBaseline="middle";const g=u+2*c,x=p+2*c,m=function(t,o,e,r,n){return"left"===t?o-n:"right"===t?o-e-n:o-r/2}(y.align,y.x,u,g,c),w=1===r.length?y.y:y.y-p/2+h,b=w+(r.length-1)*f/2;void 0!==n&&a(t,n,m,b-x/2,g,x,i),t.fillStyle=s;for(let o=0;o<r.length;o++)t.fillText(r[o],y.x,w+o*f)}const l=(t,o)=>o.flow===t.flow?t.index-o.index:o.flow-t.flow;function h(t,{size:o,priority:e,column:r}){const i=new Map;for(let o=0;o<t.length;o++){const{from:e,to:r,flow:n}=t[o],a=i.get(e)??{from:[],in:0,key:e,out:0,size:0,to:[]},s=(e===r?a:i.get(r))??{from:[],in:0,key:r,out:0,size:0,to:[]};a.out+=n,a.to.push({addY:0,flow:n,index:o,key:r,node:s}),1===a.to.length&&i.set(e,a),s.in+=n,s.from.push({addY:0,flow:n,index:o,key:e,node:a}),1===s.from.length&&i.set(r,s)}return((t,o)=>{const e=n(o);for(const o of t.values())o.from.sort(l),o.to.sort(l),o.size=Math[e](o.in||o.out,o.out||o.in)})(i,o),((t,o)=>{if(o)for(const e of t.values())e.key in o&&(e.priority=o[e.key])})(i,e),((t,o)=>{if(o)for(const e of t.values())e.key in o&&(e.column=!0,e.x=o[e.key])})(i,r),i}const c=1e-6;function d(t){return t.x??0}function f(t){return t.y??0}const u=(t,o=new Set)=>{const e=[];for(const r of t)o.has(r.key)||(o.add(r.key),e.push(r.key,...u(r.to.map(t=>t.node),o)));return e},p=(t,o)=>{const e=o.filter(t=>0===t.from.length),r=e.map(t=>t.key),n=u(e),i=new Set(n);for(const o of t)i.has(o.from)||i.has(o.to)||(r.push(o.from),i.add(o.from)),i.add(o.to);return r},y=(t,o)=>{const e=new Set(t.filter(t=>o.has(t.from)).map(t=>t.to)),r=[...o],n=r.filter(t=>!e.has(t));return n.length?n:r.slice(0,1)};function g(t,o,e=new Set){let r=0;for(const n of t)e.has(n.node)||(e.add(n.node),r+=n.node[o].length+g(n.node[o],o,e));return r}const x=t=>(o,e)=>g(o.node[t],t)-g(e.node[t],t)||o.node[t].length-e.node[t].length;function m(t,o){if(!t.from.length)return o;t.from.sort(x("from"));for(const e of t.from){const t=e.node;r(t.y)||(t.y=o,m(t,o?o+c:0)),o=Math.max(t.y+t.out,o)}return f(t)+t.size}const w=(t,o)=>Boolean(o&&d(o)<d(t));function b(t,o){if(!t.to.length)return o;t.to.sort(x("to"));for(let e=0;e<t.to.length;e++){const n=t.to[e],i=n.node;r(i.y)||(i.y=o,b(i,o?o+c:0)),w(i,t.to[e+1]?.node)?o+=n.flow:o=Math.max(i.y+Math.max(i.in,i.out),o)}return f(t)+t.size}function M(t,o){return r(t.y)?t.y:(t.y=o,o)}function v(t,o){if(!t.length)return 0;const e=((t,o)=>{const e=[...t].sort((t,o)=>t.size-o.size),r=e[e.length-1].size,n=t.filter(t=>t.size===r),i=n[0];if(1===n.length)return i;if(n.sort((t,o)=>d(t)-d(o)),0===d(i))return i;const a=n[n.length-1];return d(a)===o?a:n[Math.floor(n.length/2)]})(t,o);return e.y=0,m(e,0),b(e,0),function(t,o){const e=t.filter(t=>0===t.x),n=t.filter(t=>t.x===o),i=e.filter(t=>!r(t.y)),a=n.filter(t=>!r(t.y)),s=t.filter(t=>d(t)>0&&d(t)<o&&!r(t.y));let l=e.reduce((t,o)=>Math.max(t,f(o)+o.out||0),0)+c,h=n.reduce((t,o)=>Math.max(t,f(o)+o.in||0),0)+c,u=0;l>=h?(i.forEach(t=>{l=M(t,l),l=Math.max(l+t.out,b(t,l))}),a.forEach(t=>{h=M(t,h),h=Math.max(h+t.in,m(t,h))})):(i.forEach(t=>{l=M(t,l)}),a.forEach(t=>{h=M(t,h),h=Math.max(h+t.in,m(t,h))})),s.forEach(o=>{let e=t.filter(t=>d(t)===d(o)&&r(t.y)).reduce((t,o)=>Math.max(t,f(o)+Math.max(o.in,o.out)),0);e=M(o,e),e=Math.max(e+o.in,m(o,e)),e=Math.max(e+o.out,b(o,e)),u=Math.max(u,e)}),Math.max(l,h,u)}(t,o),((t,o)=>{let e=0;for(let r=0;r<=o;r++){const o=t.filter(t=>d(t)===r).sort((t,o)=>f(t)-f(o));let n=0;for(const t of o)f(t)<n&&(t.y=n),n=f(t)+t.size;e=Math.max(e,n)}return e})(t,o)}const k=(t,o)=>d(t)!==d(o)?d(t)-d(o):f(t)===f(o)?t.size-o.size:f(t)-f(o);function C(t,o,{priority:e,height:n,nodePadding:i,modeX:a}){const s=[...t.values()],l=function(t,o,e){const n=o.filter(t=>t.from!==t.to),i=[...t.keys()],a=[...t.values()],s=new Set(i);let l=0;for(;s.size;){const e=0===l?p(o,a):y(n,s);if(!e.length)throw new Error("Fatal error: Unable to place nodes to columns. Please report this issue.");for(const o of e){const e=t.get(o);e&&!r(e.x)&&(e.x=l),s.delete(o)}s.size&&l++}const h=a.reduce((t,o)=>Math.max(t,d(o)),0);if("edge"===e){const e=new Set(o.map(t=>t.from));i.filter(t=>!e.has(t)).forEach(o=>{const e=t.get(o);e&&!e.column&&(e.x=h)})}return h}(t,o,a??"edge"),h=e?function(t,o){let e=0,r=0;for(let n=0;n<=o;n++){let o=r;const i=t.filter(t=>d(t)===n).sort((t,o)=>(t.priority??0)-(o.priority??0));if(i.length){const o=t.reduce((t,o)=>d(o)>n?Math.min(t,d(o)):t,1/0);r=i[0].to.filter(t=>d(t.node)>o).reduce((t,o)=>t+o.flow,0)||0}for(const t of i)t.y=o,o+=Math.max(t.out,t.in);e=Math.max(o,e)}return e}(s,l):v(s,l),c=function(t,o){let e=0;const r=new Map,n=[],i=t=>{if(!r.has(t)){const o=n.length;return r.set(t,o),n.push([]),o}return r.get(t)??0};t.sort(k);for(const r of t){const t=i(d(r)),a=n[t]??[];if(f(r)){a.push(f(r));let e=a.length;if(r.in){for(let o=0;o<t;o++){const t=n[o]??[];for(let o=0;o<t.length&&!(t[o]>f(r));o++)e=Math.max(o+1,e)}for(;a.length<e;)a.push(f(r))}r.y=f(r)+e*o}e=Math.max(e,f(r)+Math.max(r.in,r.out))}return e}(s,h/n*i);return function(t){t.forEach(t=>{const o=t.size,e=o<t.in,r=o<t.out;let n=0,i=t.from.length;t.from.sort((t,o)=>f(t.node)+t.node.out/2-(f(o.node)+o.node.out/2)).forEach((t,r)=>{e?t.addY=r*(o-t.flow)/(i-1):(t.addY=n,n+=t.flow)}),n=0,i=t.to.length,t.to.sort((t,o)=>f(t.node)+t.node.in/2-(f(o.node)+o.node.in/2)).forEach((t,e)=>{r?t.addY=e*(o-t.flow)/(i-1):(t.addY=n,n+=t.flow)})})}(s),{maxX:l,maxY:c}}function _(t){return t.x??0}function P(t){return t.y??0}function T(t,o){return Math[o](t.in||t.out,t.out||t.in)}function S(t,o,e,r){return"vertical"===r?o<(e.top+e.bottom)/2?"bottom":"top":t<(e.left+e.right)/2?"right":"left"}function z(t,o,e){for(const r of t)if(r.key===o&&r.index===e)return r.addY;return 0}function F(t,o,e,r,n,i,a,s,l){return"vertical"===l?{_custom:{flow:n,from:t,height:a.parse(n,i),to:o,x:a.parse(r,i),y:s.parse(_(o),i)},x:a.parse(e,i),y:s.parse(_(t),i)}:{_custom:{flow:n,from:t,height:s.parse(n,i),to:o,x:a.parse(_(o),i),y:s.parse(r,i)},x:a.parse(_(t),i),y:s.parse(e,i)}}function E(t,o,e,r,n,i,a,s){const l=t._custom,h=o.getPixelForValue(t.x),c=e.getPixelForValue(t.y);return"vertical"===s?{flow:l.flow,from:l.from,height:0,to:l.to,width:Math.abs(o.getPixelForValue(t.x+l.height)-h),x:h,x2:o.getPixelForValue(l.x),y:R(e,t.y,r,i)+n+a,y2:R(e,l.y,r,i)-a}:{flow:l.flow,from:l.from,height:Math.abs(e.getPixelForValue(t.y+l.height)-c),to:l.to,width:0,x:R(o,t.x,r,i)+n+a,x2:R(o,l.x,r,i)-a,y:c,y2:e.getPixelForValue(l.y)}}function R(t,o,e,r){const n=t.getPixelForValue(o);return e?n-o/e*r:n}function W(t,o,e){const r="vertical"===o?e.height-e.chartArea.bottom:e.width-e.chartArea.right;return Math.max(0,t+3-r)}function X(t,o,e,r,n,i,a,s){if("vertical"===s){const s=e.getPixelForValue(P(t));return{height:i,width:Math.abs(e.getPixelForValue(P(t)+o)-s),x:s,y:R(r,_(t),n,a)}}const l=r.getPixelForValue(P(t));return{height:Math.abs(r.getPixelForValue(P(t)+o)-l),width:i,x:R(e,_(t),n,a),y:l}}function Y(t,o){const{backgroundColor:e,borderRadius:r=0,color:n,display:a,font:s,padding:l=4,position:h}=t.nodeLabels??{};return{backgroundColor:i(e,o),borderRadius:r,color:i(n,o)??t.color??"black",display:i(a,o)??!0,font:s,padding:l,position:i(h,o)??"auto"}}class j extends o.DatasetController{parseObjectData(t,o,e,r){const n=((t,o)=>{const{from:e="from",to:r="to",flow:n="flow"}=o;return t.map(({[e]:t,[r]:o,[n]:i})=>({flow:i,from:t,to:o}))})(o,this.options.parsing),{xScale:i,yScale:a}=t,s=[],l=h(n,this.options),c=this.options.orientation??"horizontal";this._nodes=l;const{maxX:d,maxY:f}=C(l,n,{height:"vertical"===c?this.chart.width:this.chart.height,modeX:this.options.modeX,nodePadding:this.options.nodePadding??10,priority:!!this.options.priority});if(this._maxX=d,this._maxY=f,!i||!a)return[];for(let t=0,o=n.length;t<o;++t){const o=n[t],e=l.get(o.from),r=l.get(o.to);if(!e||!r)continue;const h=P(e)+z(e.to,o.to,t),d=P(r)+z(r.from,o.from,t);s.push(F(e,r,h,d,o.flow,t,i,a,c))}return s.slice(e,e+r)}getMinMax(t){return{max:t===("vertical"===this.options.orientation?this._cachedMeta.yScale:this._cachedMeta.xScale)?this._maxX:this._maxY,min:0}}update(t){const{data:o}=this._cachedMeta;this.updateElements(o,0,o.length,t)}updateElements(t,o,e,r){const{xScale:n,yScale:i}=this._cachedMeta;if(!n||!i)return;const a=this.resolveDataElementOptions(o,r),s=this.getSharedOptions(a),{borderWidth:l,nodeWidth:h=10,orientation:c="horizontal"}=this.options,d=W(h,c,this.chart),f=l?l/2+.5:0;for(let a=o;a<o+e;a++){const o=this.getParsed(a);this.updateElement(t[a],a,{options:this.resolveDataElementOptions(a,r),progress:"reset"===r?0:1,...E(o,n,i,this._maxX,h,d,f,c)},r)}s&&this.updateSharedOptions(s,r,a)}_drawLabels(){const t=this.chart.ctx,r=this.options,i=this._nodes||new Map,a=n(r.size),l=r.labels,{borderWidth:h=1,nodeWidth:c=10,orientation:d="horizontal"}=r,f=W(c,d,this.chart),u=r.font??this.chart.options.font??o.Chart.defaults.font,{xScale:p,yScale:y}=this._cachedMeta;if(!p||!y)return;t.save();const g=this.chart.chartArea;for(const o of i.values()){const n=T(o,a),{height:i,width:x,x:m,y:w}=X(o,n,p,y,this._maxX,c,f,d),b=l?.[o.key]??o.key,M=Y(r,o);if(M.display){const o=e.toFont(M.font??u);s(t,b,{autoPosition:S(m,w,g,d),backgroundColor:M.backgroundColor,borderRadius:M.borderRadius,borderWidth:h,color:M.color,font:o,height:i,lineOffset:e.valueOrDefault(r.padding,o.lineHeight/2),padding:M.padding,position:M.position,width:x,x:m,y:w})}}t.restore()}_drawNodes(){const t=this.chart.ctx,o=this._nodes||new Map,{borderColor:e,borderWidth:r=0,nodeWidth:i=10,orientation:a="horizontal",size:s}=this.options,l=W(i,a,this.chart),h=n(s),{xScale:c,yScale:d}=this._cachedMeta;t.save(),e&&r&&(t.strokeStyle=e,t.lineWidth=r);for(const e of o.values()){if(t.fillStyle=e.color??"black",!c||!d)return;const o=Math[h](e.in||e.out,e.out||e.in),{height:n,width:s,x:f,y:u}=X(e,o,c,d,this._maxX,i,l,a);r&&t.strokeRect(f,u,s,n),t.fillRect(f,u,s,n)}t.restore()}draw(){const t=this.chart.ctx,o=this.getMeta().data??[],e=[];for(let t=0,r=o.length;t<r;++t){const r=o[t];r.from&&r.to&&(r.from.color=r.options.colorFrom,r.to.color=r.options.colorTo,r.active&&e.push(r))}for(const t of e)t.from&&t.to&&(t.from.color=t.options.colorFrom,t.to.color=t.options.colorTo);this._drawNodes();for(let e=0,r=o.length;e<r;++e)o[e].draw(t);this._drawLabels()}constructor(...t){super(...t),this._nodes=new Map,this._maxX=0,this._maxY=0}}j.id="sankey",j.descriptors={_indexable:!1,_scriptable:!0,nodeLabels:{_indexable:!1,_scriptable:!1}},j.defaults={animations:{colors:{properties:["colorFrom","colorTo"],type:"color"},numbers:{properties:["x","y","x2","y2","height","width"],type:"number"},progress:{delay:t=>"data"===t.type?500*t.parsed["vertical"===t.dataset.orientation?"y":"x"]+20*t.dataIndex:void 0,duration:t=>"data"===t.type?200*(t.parsed._custom["vertical"===t.dataset.orientation?"y":"x"]-t.parsed["vertical"===t.dataset.orientation?"y":"x"]):void 0,easing:"linear"}},borderColor:"black",borderWidth:1,color:"black",dataElementType:"flow",modeX:"edge",nodePadding:10,nodeWidth:10,orientation:"horizontal",transitions:{hide:{animations:{colors:{properties:["colorFrom","colorTo"],to:"transparent",type:"color"}}},resize:{animations:{progress:{delay:0,duration:0}}},show:{animations:{colors:{from:"transparent",properties:["colorFrom","colorTo"],type:"color"}}}}},j.overrides={datasets:{clip:!1,parsing:{flow:"flow",from:"from",to:"to"}},interaction:{intersect:!0,mode:"nearest"},layout:{padding:{bottom:3,left:3,right:13,top:3}},plugins:{legend:{display:!1},tooltip:{callbacks:{label(t){const o=t.parsed._custom;return`${o.from.key} -> ${o.to.key}: ${o.flow}`},title:()=>""}}},scales:{x:{bounds:"data",display:!1,min:0,offset:!1,type:"linear"},y:{bounds:"data",display:!1,min:0,offset:!1,reverse:!0,type:"linear"}}};const O=(t,o,e,r,n)=>"vertical"===n?o<r?{cp1:{x:t,y:o+(r-o)/3*2},cp2:{x:e,y:o+(r-o)/3}}:{cp1:{x:0,y:o-(o-r)/3},cp2:{x:0,y:r+(o-r)/3}}:t<e?{cp1:{x:t+(e-t)/3*2,y:o},cp2:{x:t+(e-t)/3,y:r}}:{cp1:{x:t-(t-e)/3,y:0},cp2:{x:e+(t-e)/3,y:0}},V=(t,o,e)=>({x:t.x+e*(o.x-t.x),y:t.y+e*(o.y-t.y)}),L=(t,o)=>e.color(t).alpha(o).rgbString(),A=(t,o)=>"string"==typeof t?L(t,o):t,q=t=>"string"==typeof t?e.getHoverColor(t):t;class D extends o.Element{draw(t){const{x:r,x2:n,y:i,y2:a,height:l,progress:h,width:c}=this,d=this.options.orientation,f=O(r,i,n,a,d),u={height:l,width:c,x:r,x2:n,y:i,y2:a};if(0===h)return;t.save(),h<1&&function(t,{height:o,width:e,x:r,x2:n,y:i,y2:a},s,l){t.beginPath(),"vertical"===l?t.rect(Math.min(r,n),i,Math.abs(n-r)+e+1,(a-i)*s+1):t.rect(r,Math.min(i,a),(n-r)*s+1,Math.abs(a-i)+o+1),t.clip()}(t,u,h,d),function(t,{x:o,x2:e,y:r,y2:n,options:i}){let a="black";null!==i.flowColor?a=i.flowColor:"from"===i.colorMode?a=A(i.colorFrom,i.alpha):"to"===i.colorMode?a=A(i.colorTo,i.alpha):"string"==typeof i.colorFrom&&"string"==typeof i.colorTo&&(a="vertical"===i.orientation?t.createLinearGradient(0,r,0,n):t.createLinearGradient(o,0,e,0),a.addColorStop(0,L(i.colorFrom,i.alpha)),a.addColorStop(1,L(i.colorTo,i.alpha))),t.fillStyle=a,t.strokeStyle=a,t.lineWidth=.5}(t,this),function(t,{height:o,width:e,x:r,x2:n,y:i,y2:a},{cp1:s,cp2:l},h){t.beginPath(),t.moveTo(r,i),t.bezierCurveTo(s.x,s.y,l.x,l.y,n,a),"vertical"===h?(t.lineTo(n+e,a),t.bezierCurveTo(l.x+e,l.y,s.x+e,s.y,r+e,i)):(t.lineTo(n,a+o),t.bezierCurveTo(l.x,l.y+o,s.x,s.y+o,r,i+o)),t.lineTo(r,i),t.stroke(),t.closePath(),t.fill()}(t,u,f,d);const p=this.options.flowLabels;if(p.display){const r=e.toFont(p.font??o.Chart.defaults.font),n=function({height:t,width:o,x:e,x2:r,y:n,y2:i},a){return"vertical"===a?{height:i-n,width:Math.abs(r-e)+o,x:Math.min(e,r),y:n}:{height:Math.abs(i-n)+t,width:r-e,x:e,y:Math.min(n,i)}}(u,d);s(t,`${this.flow}`,{autoPosition:"center",backgroundColor:p.backgroundColor,borderRadius:p.borderRadius,borderWidth:0,color:p.color,font:r,height:n.height,lineOffset:r.lineHeight/2,padding:p.padding,position:p.position,width:n.width,x:n.x,y:n.y})}t.restore()}inRange(t,o,e){const{x:r,y:n,x2:i,y2:a,height:s,width:l}=this.getProps(["x","y","x2","y2","height","width"],e),h="vertical"===this.options.orientation;if(h?o<n||o>a:t<r||t>i)return!1;const{cp1:c,cp2:d}=O(r,n,i,a,this.options.orientation),f=h?(o-n)/(a-n):(t-r)/(i-r),u={x:i,y:a},p=V({x:r,y:n},c,f),y=V(c,d,f),g=V(d,u,f),x=V(p,y,f),m=V(y,g,f),w=V(x,m,f);return h?t>=w.x&&t<=w.x+l:o>=w.y&&o<=w.y+s}inXRange(t,o){const{x:e,x2:r,width:n}=this.getProps(["x","x2","width"],o),i=Math.min(e,r),a=Math.max(e,r)+("vertical"===this.options.orientation?n:0);return t>=i&&t<=a}inYRange(t,o){const{y:e,y2:r,height:n}=this.getProps(["y","y2","height"],o),i=Math.min(e,r),a=Math.max(e,r)+("vertical"===this.options.orientation?0:n);return t>=i&&t<=a}getCenterPoint(t){const{x:o,y:e,x2:r,y2:n,height:i,width:a}=this.getProps(["x","y","x2","y2","height","width"],t),s="vertical"===this.options.orientation;return{x:(o+r+(s?a:0))/2,y:(e+n+(s?0:i))/2}}tooltipPosition(t=!1){return this.getCenterPoint(t)}getRange(t){const o="vertical"===this.options.orientation;return"x"===t?o?this.width/2:0:o?0:this.height/2}constructor(t){super(),this.flow=0,this.x2=0,this.y2=0,this.width=0,this.height=0,this.progress=1,t&&Object.assign(this,t)}}D.id="flow",D.defaults={alpha:.5,colorFrom:"red",colorMode:"gradient",colorTo:"green",flowColor:null,flowLabels:{borderRadius:0,color:"black",display:!1,padding:4,position:"center"},hoverColorFrom:(t,o)=>q(o.colorFrom),hoverColorTo:(t,o)=>q(o.colorTo),orientation:"horizontal"},D.descriptors={_scriptable:!0,flowLabels:{_scriptable:!0}},o.Chart.register(j,D),t.Flow=D,t.SankeyController=j});
|
|
7
|
+
!function(t,o){"object"==typeof exports&&"undefined"!=typeof module?o(exports,require("chart.js"),require("chart.js/helpers")):"function"==typeof define&&define.amd?define(["exports","chart.js","chart.js/helpers"],o):o((t="undefined"!=typeof globalThis?globalThis:t||self)["chartjs-chart-sankey"]={},t.Chart,t.Chart.helpers)}(this,function(t,o,e){"use strict";const r=t=>void 0!==t;function n(t){return t&&-1!==["min","max"].indexOf(t)?t:"max"}function i(t,o){return"function"==typeof t?t(o):t&&"object"==typeof t&&!function(t){const o=Object.prototype.toString.call(t);return"[object CanvasPattern]"===o||"[object CanvasGradient]"===o}(t)?t[o.key]:t}function a(t,o,e,r,n,i,a){t.save(),t.fillStyle=o,a>0?(!function(t,o,e,r,n,i){const a=Math.max(0,Math.min(i,r/2,n/2));t.beginPath(),t.moveTo(o+a,e),t.lineTo(o+r-a,e),t.quadraticCurveTo(o+r,e,o+r,e+a),t.lineTo(o+r,e+n-a),t.quadraticCurveTo(o+r,e+n,o+r-a,e+n),t.lineTo(o+a,e+n),t.quadraticCurveTo(o,e+n,o,e+n-a),t.lineTo(o,e+a),t.quadraticCurveTo(o,e,o+a,e),t.closePath()}(t,e,r,n,i,a),t.fill()):t.fillRect(e,r,n,i),t.restore()}function s(t,o,e){const r=function(t){if(!t)return[];const o=[],e=Array.isArray(t)?t:[t];for(;e.length;){const t=e.pop();"string"==typeof t?o.unshift(...t.split("\n")):Array.isArray(t)?e.push(...t):t&&o.unshift(`${t}`)}return o}(o);if(!r.length)return;const{backgroundColor:n,borderRadius:i,color:s,font:l,lineOffset:h,padding:c}=e,f=function(t,o){return"auto"===t?o:t}(e.position,e.autoPosition),d=Number(l.lineHeight);t.font=l.string;const u=Math.max(...r.map(o=>t.measureText(o).width)),p=d*r.length,y=function(t,o,e){const{borderWidth:r,height:n,padding:i,width:a,x:s,y:l}=o,h={align:"center",x:s+a/2,y:l+n/2};return"left"===t?(h.align="right",h.x=s-r-i):"right"===t?(h.align="left",h.x=s+a+r+i):"top"===t?h.y=l-i-e/2:"bottom"===t&&(h.y=l+n+i+e/2),h}(f,e,p);t.textAlign=y.align,t.textBaseline="middle";const g=u+2*c,x=p+2*c,m=function(t,o,e,r,n){return"left"===t?o-n:"right"===t?o-e-n:o-r/2}(y.align,y.x,u,g,c),b=1===r.length?y.y:y.y-p/2+h,w=b+(r.length-1)*d/2;void 0!==n&&a(t,n,m,w-x/2,g,x,i),t.fillStyle=s;for(let o=0;o<r.length;o++)t.fillText(r[o],y.x,b+o*d)}const l=(t,o)=>o.flow===t.flow?t.index-o.index:o.flow-t.flow;function h(t,{size:o,priority:e,column:r}){const i=new Map;for(let o=0;o<t.length;o++){const{from:e,to:r,flow:n}=t[o],a=i.get(e)??{from:[],in:0,key:e,out:0,size:0,to:[]},s=(e===r?a:i.get(r))??{from:[],in:0,key:r,out:0,size:0,to:[]};a.out+=n,a.to.push({addY:0,flow:n,index:o,key:r,node:s}),1===a.to.length&&i.set(e,a),s.in+=n,s.from.push({addY:0,flow:n,index:o,key:e,node:a}),1===s.from.length&&i.set(r,s)}return((t,o)=>{const e=n(o);for(const o of t.values())o.from.sort(l),o.to.sort(l),o.size=Math[e](o.in||o.out,o.out||o.in)})(i,o),((t,o)=>{if(o)for(const e of t.values())e.key in o&&(e.priority=o[e.key])})(i,e),((t,o)=>{if(o)for(const e of t.values())e.key in o&&(e.column=!0,e.x=o[e.key])})(i,r),i}const c=1e-6;function f(t){return t.x??0}function d(t){return t.y??0}const u=(t,o=new Set)=>{const e=[];for(const r of t)o.has(r.key)||(o.add(r.key),e.push(r.key,...u(r.to.map(t=>t.node),o)));return e},p=(t,o)=>{const e=o.filter(t=>0===t.from.length),r=e.map(t=>t.key),n=u(e),i=new Set(n);for(const o of t)i.has(o.from)||i.has(o.to)||(r.push(o.from),i.add(o.from)),i.add(o.to);return r},y=(t,o)=>{const e=new Set(t.filter(t=>o.has(t.from)).map(t=>t.to)),r=[...o],n=r.filter(t=>!e.has(t));return n.length?n:r.slice(0,1)};function g(t,o,e=new Set){let r=0;for(const n of t)e.has(n.node)||(e.add(n.node),r+=n.node[o].length+g(n.node[o],o,e));return r}const x=t=>(o,e)=>g(o.node[t],t)-g(e.node[t],t)||o.node[t].length-e.node[t].length;function m(t,o){if(!t.from.length)return o;t.from.sort(x("from"));for(const e of t.from){const t=e.node;r(t.y)||(t.y=o,m(t,o?o+c:0)),o=Math.max(t.y+t.out,o)}return d(t)+t.size}const b=(t,o)=>Boolean(o&&f(o)<f(t));function w(t,o){if(!t.to.length)return o;t.to.sort(x("to"));for(let e=0;e<t.to.length;e++){const n=t.to[e],i=n.node;r(i.y)||(i.y=o,w(i,o?o+c:0)),b(i,t.to[e+1]?.node)?o+=n.flow:o=Math.max(i.y+Math.max(i.in,i.out),o)}return d(t)+t.size}function M(t,o){return r(t.y)?t.y:(t.y=o,o)}function v(t,o){if(!t.length)return 0;const e=((t,o)=>{const e=[...t].sort((t,o)=>t.size-o.size),r=e[e.length-1].size,n=t.filter(t=>t.size===r),i=n[0];if(1===n.length)return i;if(n.sort((t,o)=>f(t)-f(o)),0===f(i))return i;const a=n[n.length-1];return f(a)===o?a:n[Math.floor(n.length/2)]})(t,o);return e.y=0,m(e,0),w(e,0),function(t,o){const e=t.filter(t=>0===t.x),n=t.filter(t=>t.x===o),i=e.filter(t=>!r(t.y)),a=n.filter(t=>!r(t.y)),s=t.filter(t=>f(t)>0&&f(t)<o&&!r(t.y));let l=e.reduce((t,o)=>Math.max(t,d(o)+o.out||0),0)+c,h=n.reduce((t,o)=>Math.max(t,d(o)+o.in||0),0)+c,u=0;l>=h?(i.forEach(t=>{l=M(t,l),l=Math.max(l+t.out,w(t,l))}),a.forEach(t=>{h=M(t,h),h=Math.max(h+t.in,m(t,h))})):(i.forEach(t=>{l=M(t,l)}),a.forEach(t=>{h=M(t,h),h=Math.max(h+t.in,m(t,h))})),s.forEach(o=>{let e=t.filter(t=>f(t)===f(o)&&r(t.y)).reduce((t,o)=>Math.max(t,d(o)+Math.max(o.in,o.out)),0);e=M(o,e),e=Math.max(e+o.in,m(o,e)),e=Math.max(e+o.out,w(o,e)),u=Math.max(u,e)}),Math.max(l,h,u)}(t,o),((t,o)=>{let e=0;for(let r=0;r<=o;r++){const o=t.filter(t=>f(t)===r).sort((t,o)=>d(t)-d(o));let n=0;for(const t of o)d(t)<n&&(t.y=n),n=d(t)+t.size;e=Math.max(e,n)}return e})(t,o)}const k=(t,o)=>f(t)!==f(o)?f(t)-f(o):d(t)===d(o)?t.size-o.size:d(t)-d(o);function C(t,o,e,r){let n=r;for(let r=0;r<o;r++){const o=t[r].yHistory;for(let t=0;t<o.length&&!(o[t]>e);t++)n=Math.max(t+1,n)}return n}function P(t,o,e,r){const n=t.realCount,i=o.before*r,a=o.after*r,s=n>0?Math.max(t.lastAfter,i):0,l=t.realCumOffset+s,h=e-n;return t.realCount=n+1,t.realCumOffset=l,t.lastAfter=a,l+h*i}function _(t,o,e=1,r="auto"){if(t.sort(k),"even"===r)return function(t,o,e){let r,n,i,a=0;for(const s of t){const t=f(s),l=o.get(s.key)??{after:0,before:0};t!==r?r=t:n&&i&&(s.y=d(n)+n.size+Math.max(i.after,l.before)*e),n=s,i=l,a=Math.max(a,d(s)+Math.max(s.in,s.out))}return a}(t,o,e);let n=0;const i=new Map,a=[],s=t=>{if(!i.has(t)){const o=a.length;return i.set(t,o),a.push({lastAfter:0,realCount:0,realCumOffset:0,yHistory:[]}),o}return i.get(t)??0};for(const r of t){const t=s(f(r)),i=a[t],l=o.get(r.key)??{after:0,before:0},h=d(r);if(h){i.yHistory.push(h);let o=i.yHistory.length;if(r.in)for(o=C(a,t,h,o);i.yHistory.length<o;)i.yHistory.push(h);r.y=h+P(i,l,o,e)}else i.realCount+=1,i.lastAfter=l.after*e;n=Math.max(n,d(r)+Math.max(r.in,r.out))}return n}function T(t,o,{priority:e,height:n,nodePadding:i,nodePaddingMode:a,modeX:s}){const l=[...t.values()],h=function(t,o,e){const n=o.filter(t=>t.from!==t.to),i=[...t.keys()],a=[...t.values()],s=new Set(i);let l=0;for(;s.size;){const e=0===l?p(o,a):y(n,s);if(!e.length)throw new Error("Fatal error: Unable to place nodes to columns. Please report this issue.");for(const o of e){const e=t.get(o);e&&!r(e.x)&&(e.x=l),s.delete(o)}s.size&&l++}const h=a.reduce((t,o)=>Math.max(t,f(o)),0);if("edge"===e){const e=new Set(o.map(t=>t.from));i.filter(t=>!e.has(t)).forEach(o=>{const e=t.get(o);e&&!e.column&&(e.x=h)})}return h}(t,o,s??"edge"),c=e?function(t,o){let e=0,r=0;for(let n=0;n<=o;n++){let o=r;const i=t.filter(t=>f(t)===n).sort((t,o)=>(t.priority??0)-(o.priority??0));if(i.length){const o=t.reduce((t,o)=>f(o)>n?Math.min(t,f(o)):t,1/0);r=i[0].to.filter(t=>f(t.node)>o).reduce((t,o)=>t+o.flow,0)||0}for(const t of i)t.y=o,o+=Math.max(t.out,t.in);e=Math.max(o,e)}return e}(l,h):v(l,h),u=_(l,i,c/n,a??"auto");return function(t){t.forEach(t=>{const o=t.size,e=o<t.in,r=o<t.out;let n=0,i=t.from.length;t.from.sort((t,o)=>d(t.node)+t.node.out/2-(d(o.node)+o.node.out/2)).forEach((t,r)=>{e?t.addY=r*(o-t.flow)/(i-1):(t.addY=n,n+=t.flow)}),n=0,i=t.to.length,t.to.sort((t,o)=>d(t.node)+t.node.in/2-(d(o.node)+o.node.in/2)).forEach((t,e)=>{r?t.addY=e*(o-t.flow)/(i-1):(t.addY=n,n+=t.flow)})})}(l),{maxX:h,maxY:u}}function z(t){return t.x??0}function S(t){return t.y??0}function F(t,o){return Math[o](t.in||t.out,t.out||t.in)}function E(t,o,e,r){return"vertical"===r?o<(e.top+e.bottom)/2?"bottom":"top":t<(e.left+e.right)/2?"right":"left"}function O(t,o,e){for(const r of t)if(r.key===o&&r.index===e)return r.addY;return 0}function R(t,o,e,r,n,i,a,s,l){return"vertical"===l?{_custom:{flow:n,from:t,height:a.parse(n,i),to:o,x:a.parse(r,i),y:s.parse(z(o),i)},x:a.parse(e,i),y:s.parse(z(t),i)}:{_custom:{flow:n,from:t,height:s.parse(n,i),to:o,x:a.parse(z(o),i),y:s.parse(r,i)},x:a.parse(z(t),i),y:s.parse(e,i)}}function W(t,o,e,r,n,i,a,s){const l=t._custom,h=o.getPixelForValue(t.x),c=e.getPixelForValue(t.y);return"vertical"===s?{flow:l.flow,from:l.from,height:0,to:l.to,width:Math.abs(o.getPixelForValue(t.x+l.height)-h),x:h,x2:o.getPixelForValue(l.x),y:X(e,t.y,r,i)+n+a,y2:X(e,l.y,r,i)-a}:{flow:l.flow,from:l.from,height:Math.abs(e.getPixelForValue(t.y+l.height)-c),to:l.to,width:0,x:X(o,t.x,r,i)+n+a,x2:X(o,l.x,r,i)-a,y:c,y2:e.getPixelForValue(l.y)}}function X(t,o,e,r){const n=t.getPixelForValue(o);return e?n-o/e*r:n}function Y(t,o,e){const r="vertical"===o?e.height-e.chartArea.bottom:e.width-e.chartArea.right;return Math.max(0,t+3-r)}function j(t,o,e,r,n,i,a,s){if("vertical"===s){const s=e.getPixelForValue(S(t));return{height:i,width:Math.abs(e.getPixelForValue(S(t)+o)-s),x:s,y:X(r,z(t),n,a)}}const l=r.getPixelForValue(S(t));return{height:Math.abs(r.getPixelForValue(S(t)+o)-l),width:i,x:X(e,z(t),n,a),y:l}}function A(t,o){const e=i(t??10,o)??10;return"number"==typeof e?{after:e,before:e}:{after:e.after??10,before:e.before??10}}function V(t,o){const{backgroundColor:e,borderRadius:r=0,color:n,display:a,font:s,padding:l=4,position:h}=t.nodeLabels??{};return{backgroundColor:i(e,o),borderRadius:r,color:i(n,o)??t.color??"black",display:i(a,o)??!0,font:s,padding:l,position:i(h,o)??"auto"}}class H extends o.DatasetController{parseObjectData(t,o,e,r){const n=((t,o)=>{const{from:e="from",to:r="to",flow:n="flow"}=o;return t.map(({[e]:t,[r]:o,[n]:i})=>({flow:i,from:t,to:o}))})(o,this.options.parsing),{xScale:i,yScale:a}=t,s=[],l=h(n,this.options),c=this.options.orientation??"horizontal";this._nodes=l;const f=new Map;for(const t of l.values())f.set(t.key,A(this.options.nodePadding,t));const{maxX:d,maxY:u}=T(l,n,{height:"vertical"===c?this.chart.width:this.chart.height,modeX:this.options.modeX,nodePadding:f,nodePaddingMode:this.options.nodePaddingMode,priority:!!this.options.priority});if(this._maxX=d,this._maxY=u,!i||!a)return[];for(let t=0,o=n.length;t<o;++t){const o=n[t],e=l.get(o.from),r=l.get(o.to);if(!e||!r)continue;const h=S(e)+O(e.to,o.to,t),f=S(r)+O(r.from,o.from,t);s.push(R(e,r,h,f,o.flow,t,i,a,c))}return s.slice(e,e+r)}getMinMax(t){return{max:t===("vertical"===this.options.orientation?this._cachedMeta.yScale:this._cachedMeta.xScale)?this._maxX:this._maxY,min:0}}update(t){const{data:o}=this._cachedMeta;this.updateElements(o,0,o.length,t)}updateElements(t,o,e,r){const{xScale:n,yScale:i}=this._cachedMeta;if(!n||!i)return;const a=this.resolveDataElementOptions(o,r),s=this.getSharedOptions(a),{borderWidth:l,nodeWidth:h=10,orientation:c="horizontal"}=this.options,f=Y(h,c,this.chart),d=l?l/2+.5:0;for(let a=o;a<o+e;a++){const o=this.getParsed(a);this.updateElement(t[a],a,{options:this.resolveDataElementOptions(a,r),progress:"reset"===r?0:1,...W(o,n,i,this._maxX,h,f,d,c)},r)}s&&this.updateSharedOptions(s,r,a)}_drawLabels(){const t=this.chart.ctx,r=this.options,i=this._nodes||new Map,a=n(r.size),l=r.labels,{borderWidth:h=1,nodeWidth:c=10,orientation:f="horizontal"}=r,d=Y(c,f,this.chart),u=r.font??this.chart.options.font??o.Chart.defaults.font,{xScale:p,yScale:y}=this._cachedMeta;if(!p||!y)return;t.save();const g=this.chart.chartArea;for(const o of i.values()){const n=F(o,a),{height:i,width:x,x:m,y:b}=j(o,n,p,y,this._maxX,c,d,f),w=l?.[o.key]??o.key,M=V(r,o);if(M.display){const o=e.toFont(M.font??u);s(t,w,{autoPosition:E(m,b,g,f),backgroundColor:M.backgroundColor,borderRadius:M.borderRadius,borderWidth:h,color:M.color,font:o,height:i,lineOffset:e.valueOrDefault(r.padding,o.lineHeight/2),padding:M.padding,position:M.position,width:x,x:m,y:b})}}t.restore()}_drawNodes(){const t=this.chart.ctx,o=this._nodes||new Map,{borderColor:e,borderWidth:r=0,nodeWidth:i=10,orientation:a="horizontal",size:s}=this.options,l=Y(i,a,this.chart),h=n(s),{xScale:c,yScale:f}=this._cachedMeta;t.save(),e&&r&&(t.strokeStyle=e,t.lineWidth=r);for(const e of o.values()){if(t.fillStyle=e.color??"black",!c||!f)return;const o=Math[h](e.in||e.out,e.out||e.in),{height:n,width:s,x:d,y:u}=j(e,o,c,f,this._maxX,i,l,a);r&&t.strokeRect(d,u,s,n),t.fillRect(d,u,s,n)}t.restore()}draw(){const t=this.chart.ctx,o=this.getMeta().data??[],e=[];for(let t=0,r=o.length;t<r;++t){const r=o[t];r.from&&r.to&&(r.from.color=r.options.colorFrom,r.to.color=r.options.colorTo,r.active&&e.push(r))}for(const t of e)t.from&&t.to&&(t.from.color=t.options.colorFrom,t.to.color=t.options.colorTo);this._drawNodes();for(let e=0,r=o.length;e<r;++e)o[e].draw(t);this._drawLabels()}constructor(...t){super(...t),this._nodes=new Map,this._maxX=0,this._maxY=0}}H.id="sankey",H.descriptors={_indexable:!1,_scriptable:t=>"nodePadding"!==t,nodeLabels:{_indexable:!1,_scriptable:!1}},H.defaults={animations:{colors:{properties:["colorFrom","colorTo"],type:"color"},numbers:{properties:["x","y","x2","y2","height","width"],type:"number"},progress:{delay:t=>"data"===t.type?500*t.parsed["vertical"===t.dataset.orientation?"y":"x"]+20*t.dataIndex:void 0,duration:t=>"data"===t.type?200*(t.parsed._custom["vertical"===t.dataset.orientation?"y":"x"]-t.parsed["vertical"===t.dataset.orientation?"y":"x"]):void 0,easing:"linear"}},borderColor:"black",borderWidth:1,color:"black",dataElementType:"flow",modeX:"edge",nodePadding:10,nodePaddingMode:"auto",nodeWidth:10,orientation:"horizontal",transitions:{hide:{animations:{colors:{properties:["colorFrom","colorTo"],to:"transparent",type:"color"}}},resize:{animations:{progress:{delay:0,duration:0}}},show:{animations:{colors:{from:"transparent",properties:["colorFrom","colorTo"],type:"color"}}}}},H.overrides={datasets:{clip:!1,parsing:{flow:"flow",from:"from",to:"to"}},interaction:{intersect:!0,mode:"nearest"},layout:{padding:{bottom:3,left:3,right:13,top:3}},plugins:{legend:{display:!1},tooltip:{callbacks:{label(t){const o=t.parsed._custom;return`${o.from.key} -> ${o.to.key}: ${o.flow}`},title:()=>""}}},scales:{x:{bounds:"data",display:!1,min:0,offset:!1,type:"linear"},y:{bounds:"data",display:!1,min:0,offset:!1,reverse:!0,type:"linear"}}};const L=(t,o,e,r,n)=>"vertical"===n?o<r?{cp1:{x:t,y:o+(r-o)/3*2},cp2:{x:e,y:o+(r-o)/3}}:{cp1:{x:0,y:o-(o-r)/3},cp2:{x:0,y:r+(o-r)/3}}:t<e?{cp1:{x:t+(e-t)/3*2,y:o},cp2:{x:t+(e-t)/3,y:r}}:{cp1:{x:t-(t-e)/3,y:0},cp2:{x:e+(t-e)/3,y:0}},q=(t,o,e)=>({x:t.x+e*(o.x-t.x),y:t.y+e*(o.y-t.y)}),D=(t,o)=>e.color(t).alpha(o).rgbString(),$=(t,o)=>"string"==typeof t?D(t,o):t,G=t=>"string"==typeof t?e.getHoverColor(t):t;class N extends o.Element{draw(t){const{x:r,x2:n,y:i,y2:a,height:l,progress:h,width:c}=this,f=this.options.orientation,d=L(r,i,n,a,f),u={height:l,width:c,x:r,x2:n,y:i,y2:a};if(0===h)return;t.save(),h<1&&function(t,{height:o,width:e,x:r,x2:n,y:i,y2:a},s,l){t.beginPath(),"vertical"===l?t.rect(Math.min(r,n),i,Math.abs(n-r)+e+1,(a-i)*s+1):t.rect(r,Math.min(i,a),(n-r)*s+1,Math.abs(a-i)+o+1),t.clip()}(t,u,h,f),function(t,{x:o,x2:e,y:r,y2:n,options:i}){let a="black";null!==i.flowColor?a=i.flowColor:"from"===i.colorMode?a=$(i.colorFrom,i.alpha):"to"===i.colorMode?a=$(i.colorTo,i.alpha):"string"==typeof i.colorFrom&&"string"==typeof i.colorTo&&(a="vertical"===i.orientation?t.createLinearGradient(0,r,0,n):t.createLinearGradient(o,0,e,0),a.addColorStop(0,D(i.colorFrom,i.alpha)),a.addColorStop(1,D(i.colorTo,i.alpha))),t.fillStyle=a,t.strokeStyle=a,t.lineWidth=.5}(t,this),function(t,{height:o,width:e,x:r,x2:n,y:i,y2:a},{cp1:s,cp2:l},h){t.beginPath(),t.moveTo(r,i),t.bezierCurveTo(s.x,s.y,l.x,l.y,n,a),"vertical"===h?(t.lineTo(n+e,a),t.bezierCurveTo(l.x+e,l.y,s.x+e,s.y,r+e,i)):(t.lineTo(n,a+o),t.bezierCurveTo(l.x,l.y+o,s.x,s.y+o,r,i+o)),t.lineTo(r,i),t.stroke(),t.closePath(),t.fill()}(t,u,d,f);const p=this.options.flowLabels;if(p.display){const r=e.toFont(p.font??o.Chart.defaults.font),n=function({height:t,width:o,x:e,x2:r,y:n,y2:i},a){return"vertical"===a?{height:i-n,width:Math.abs(r-e)+o,x:Math.min(e,r),y:n}:{height:Math.abs(i-n)+t,width:r-e,x:e,y:Math.min(n,i)}}(u,f);s(t,`${this.flow}`,{autoPosition:"center",backgroundColor:p.backgroundColor,borderRadius:p.borderRadius,borderWidth:0,color:p.color,font:r,height:n.height,lineOffset:r.lineHeight/2,padding:p.padding,position:p.position,width:n.width,x:n.x,y:n.y})}t.restore()}inRange(t,o,e){const{x:r,y:n,x2:i,y2:a,height:s,width:l}=this.getProps(["x","y","x2","y2","height","width"],e),h="vertical"===this.options.orientation;if(h?o<n||o>a:t<r||t>i)return!1;const{cp1:c,cp2:f}=L(r,n,i,a,this.options.orientation),d=h?(o-n)/(a-n):(t-r)/(i-r),u={x:i,y:a},p=q({x:r,y:n},c,d),y=q(c,f,d),g=q(f,u,d),x=q(p,y,d),m=q(y,g,d),b=q(x,m,d);return h?t>=b.x&&t<=b.x+l:o>=b.y&&o<=b.y+s}inXRange(t,o){const{x:e,x2:r,width:n}=this.getProps(["x","x2","width"],o),i=Math.min(e,r),a=Math.max(e,r)+("vertical"===this.options.orientation?n:0);return t>=i&&t<=a}inYRange(t,o){const{y:e,y2:r,height:n}=this.getProps(["y","y2","height"],o),i=Math.min(e,r),a=Math.max(e,r)+("vertical"===this.options.orientation?0:n);return t>=i&&t<=a}getCenterPoint(t){const{x:o,y:e,x2:r,y2:n,height:i,width:a}=this.getProps(["x","y","x2","y2","height","width"],t),s="vertical"===this.options.orientation;return{x:(o+r+(s?a:0))/2,y:(e+n+(s?0:i))/2}}tooltipPosition(t=!1){return this.getCenterPoint(t)}getRange(t){const o="vertical"===this.options.orientation;return"x"===t?o?this.width/2:0:o?0:this.height/2}constructor(t){super(),this.flow=0,this.x2=0,this.y2=0,this.width=0,this.height=0,this.progress=1,t&&Object.assign(this,t)}}N.id="flow",N.defaults={alpha:.5,colorFrom:"red",colorMode:"gradient",colorTo:"green",flowColor:null,flowLabels:{borderRadius:0,color:"black",display:!1,padding:4,position:"center"},hoverColorFrom:(t,o)=>G(o.colorFrom),hoverColorTo:(t,o)=>G(o.colorTo),orientation:"horizontal"},N.descriptors={_scriptable:!0,flowLabels:{_scriptable:!0}},o.Chart.register(H,N),t.Flow=N,t.SankeyController=H});
|
package/dist/controller.d.cts
CHANGED
|
@@ -6,7 +6,7 @@ export default class SankeyController extends DatasetController {
|
|
|
6
6
|
static readonly id = "sankey";
|
|
7
7
|
static readonly descriptors: {
|
|
8
8
|
_indexable: boolean;
|
|
9
|
-
_scriptable: boolean;
|
|
9
|
+
_scriptable: (name: string) => boolean;
|
|
10
10
|
nodeLabels: {
|
|
11
11
|
_indexable: boolean;
|
|
12
12
|
_scriptable: boolean;
|
|
@@ -34,6 +34,7 @@ export default class SankeyController extends DatasetController {
|
|
|
34
34
|
dataElementType: string;
|
|
35
35
|
modeX: string;
|
|
36
36
|
nodePadding: number;
|
|
37
|
+
nodePaddingMode: string;
|
|
37
38
|
nodeWidth: number;
|
|
38
39
|
orientation: string;
|
|
39
40
|
transitions: {
|
package/dist/controller.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export default class SankeyController extends DatasetController {
|
|
|
6
6
|
static readonly id = "sankey";
|
|
7
7
|
static readonly descriptors: {
|
|
8
8
|
_indexable: boolean;
|
|
9
|
-
_scriptable: boolean;
|
|
9
|
+
_scriptable: (name: string) => boolean;
|
|
10
10
|
nodeLabels: {
|
|
11
11
|
_indexable: boolean;
|
|
12
12
|
_scriptable: boolean;
|
|
@@ -34,6 +34,7 @@ export default class SankeyController extends DatasetController {
|
|
|
34
34
|
dataElementType: string;
|
|
35
35
|
modeX: string;
|
|
36
36
|
nodePadding: number;
|
|
37
|
+
nodePaddingMode: string;
|
|
37
38
|
nodeWidth: number;
|
|
38
39
|
orientation: string;
|
|
39
40
|
transitions: {
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import Sankey from './controller.cjs';
|
|
2
2
|
import Flow from './flow.cjs';
|
|
3
|
-
export type { FlowConfig, FlowOptions, FlowProps, SankeyControllerDatasetFlowLabelsOptions, SankeyControllerDatasetNodeLabelsOptions, SankeyControllerDatasetOptions, SankeyDataPoint, SankeyLabelPosition, SankeyNodeLabelOption, SankeyNodeLabelPosition, SankeyOrientation, SankeyParsedData, SankeyParsingOptions, SankeyScriptableContext, } from './types.cjs';
|
|
3
|
+
export type { FlowConfig, FlowOptions, FlowProps, SankeyControllerDatasetFlowLabelsOptions, SankeyControllerDatasetNodeLabelsOptions, SankeyControllerDatasetOptions, SankeyDataPoint, SankeyLabelPosition, SankeyNodeGap, SankeyNodeLabelOption, SankeyNodeLabelPosition, SankeyNodeOption, SankeyOrientation, SankeyParsedData, SankeyParsingOptions, SankeyScriptableContext, } from './types.cjs';
|
|
4
4
|
export { Flow, Sankey as SankeyController };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import Sankey from './controller.js';
|
|
2
2
|
import Flow from './flow.js';
|
|
3
|
-
export type { FlowConfig, FlowOptions, FlowProps, SankeyControllerDatasetFlowLabelsOptions, SankeyControllerDatasetNodeLabelsOptions, SankeyControllerDatasetOptions, SankeyDataPoint, SankeyLabelPosition, SankeyNodeLabelOption, SankeyNodeLabelPosition, SankeyOrientation, SankeyParsedData, SankeyParsingOptions, SankeyScriptableContext, } from './types.js';
|
|
3
|
+
export type { FlowConfig, FlowOptions, FlowProps, SankeyControllerDatasetFlowLabelsOptions, SankeyControllerDatasetNodeLabelsOptions, SankeyControllerDatasetOptions, SankeyDataPoint, SankeyLabelPosition, SankeyNodeGap, SankeyNodeLabelOption, SankeyNodeLabelPosition, SankeyNodeOption, SankeyOrientation, SankeyParsedData, SankeyParsingOptions, SankeyScriptableContext, } from './types.js';
|
|
4
4
|
export { Flow, Sankey as SankeyController };
|
package/dist/index.esm.d.cts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export type { FlowConfig, FlowOptions, FlowProps, SankeyControllerDatasetFlowLabelsOptions, SankeyControllerDatasetNodeLabelsOptions, SankeyControllerDatasetOptions, SankeyDataPoint, SankeyLabelPosition, SankeyNodeLabelOption, SankeyNodeLabelPosition, SankeyOrientation, SankeyParsedData, SankeyParsingOptions, SankeyScriptableContext, } from './types.cjs';
|
|
1
|
+
export type { FlowConfig, FlowOptions, FlowProps, SankeyControllerDatasetFlowLabelsOptions, SankeyControllerDatasetNodeLabelsOptions, SankeyControllerDatasetOptions, SankeyDataPoint, SankeyLabelPosition, SankeyNodeGap, SankeyNodeLabelOption, SankeyNodeLabelPosition, SankeyNodeOption, SankeyOrientation, SankeyParsedData, SankeyParsingOptions, SankeyScriptableContext, } from './types.cjs';
|
|
2
2
|
export { default as SankeyController } from './controller.cjs';
|
|
3
3
|
export { default as Flow } from './flow.cjs';
|
package/dist/index.esm.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export type { FlowConfig, FlowOptions, FlowProps, SankeyControllerDatasetFlowLabelsOptions, SankeyControllerDatasetNodeLabelsOptions, SankeyControllerDatasetOptions, SankeyDataPoint, SankeyLabelPosition, SankeyNodeLabelOption, SankeyNodeLabelPosition, SankeyOrientation, SankeyParsedData, SankeyParsingOptions, SankeyScriptableContext, } from './types.js';
|
|
1
|
+
export type { FlowConfig, FlowOptions, FlowProps, SankeyControllerDatasetFlowLabelsOptions, SankeyControllerDatasetNodeLabelsOptions, SankeyControllerDatasetOptions, SankeyDataPoint, SankeyLabelPosition, SankeyNodeGap, SankeyNodeLabelOption, SankeyNodeLabelPosition, SankeyNodeOption, SankeyOrientation, SankeyParsedData, SankeyParsingOptions, SankeyScriptableContext, } from './types.js';
|
|
2
2
|
export { default as SankeyController } from './controller.js';
|
|
3
3
|
export { default as Flow } from './flow.js';
|
package/dist/labels.d.cts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { CanvasFontSpec, Color } from 'chart.js' with { 'resolution-mode': 'import' };
|
|
2
|
-
import type { SankeyLabelPosition, SankeyNode,
|
|
3
|
-
type ResolvableNodeLabelValue = boolean | Color | SankeyLabelPosition;
|
|
2
|
+
import type { SankeyLabelPosition, SankeyNode, SankeyNodeOption } from './types.cjs';
|
|
4
3
|
type ResolvedLabelPosition = Exclude<SankeyLabelPosition, 'auto'>;
|
|
5
4
|
export interface DrawLabelOptions {
|
|
6
5
|
autoPosition: ResolvedLabelPosition;
|
|
@@ -17,6 +16,6 @@ export interface DrawLabelOptions {
|
|
|
17
16
|
x: number;
|
|
18
17
|
y: number;
|
|
19
18
|
}
|
|
20
|
-
export declare function
|
|
19
|
+
export declare function resolveNodeOption<T>(option: SankeyNodeOption<T> | undefined, node: SankeyNode): T | undefined;
|
|
21
20
|
export declare function drawLabel(ctx: CanvasRenderingContext2D, label: string, options: DrawLabelOptions): void;
|
|
22
21
|
export {};
|
package/dist/labels.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { CanvasFontSpec, Color } from 'chart.js';
|
|
2
|
-
import type { SankeyLabelPosition, SankeyNode,
|
|
3
|
-
type ResolvableNodeLabelValue = boolean | Color | SankeyLabelPosition;
|
|
2
|
+
import type { SankeyLabelPosition, SankeyNode, SankeyNodeOption } from './types.js';
|
|
4
3
|
type ResolvedLabelPosition = Exclude<SankeyLabelPosition, 'auto'>;
|
|
5
4
|
export interface DrawLabelOptions {
|
|
6
5
|
autoPosition: ResolvedLabelPosition;
|
|
@@ -17,6 +16,6 @@ export interface DrawLabelOptions {
|
|
|
17
16
|
x: number;
|
|
18
17
|
y: number;
|
|
19
18
|
}
|
|
20
|
-
export declare function
|
|
19
|
+
export declare function resolveNodeOption<T>(option: SankeyNodeOption<T> | undefined, node: SankeyNode): T | undefined;
|
|
21
20
|
export declare function drawLabel(ctx: CanvasRenderingContext2D, label: string, options: DrawLabelOptions): void;
|
|
22
21
|
export {};
|
package/dist/lib/layout.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FromToElement, SankeyControllerDatasetOptions, SankeyDataPoint, SankeyNode } from '../types.js';
|
|
1
|
+
import type { FromToElement, SankeyControllerDatasetOptions, SankeyDataPoint, SankeyNode, SankeyNodeGap } from '../types.js';
|
|
2
2
|
export type SankeyMode = 'edge' | 'even';
|
|
3
3
|
/**
|
|
4
4
|
* Get all keys the input nodes flow to, including keys of the input nodes
|
|
@@ -15,22 +15,27 @@ export declare function nodeCount(list: Array<FromToElement>, prop: FlowDirectio
|
|
|
15
15
|
export declare const returnsToNearerColumn: (current: SankeyNode, next?: SankeyNode) => boolean;
|
|
16
16
|
export declare function calculateY(nodeArray: SankeyNode[], maxX: number): number;
|
|
17
17
|
export declare function calculateYUsingPriority(nodeArray: SankeyNode[], maxX: number): number;
|
|
18
|
+
type PaddableNode = Pick<SankeyNode, 'in' | 'key' | 'out' | 'size' | 'x' | 'y'>;
|
|
19
|
+
type NodeGap = Required<SankeyNodeGap>;
|
|
20
|
+
export type NodePaddingMode = 'auto' | 'even';
|
|
18
21
|
/**
|
|
19
22
|
* @return {number} maxY
|
|
20
23
|
*/
|
|
21
|
-
export declare function addPadding(nodeArray:
|
|
24
|
+
export declare function addPadding(nodeArray: PaddableNode[], gaps: Map<string, NodeGap>, scale?: number, mode?: NodePaddingMode): number;
|
|
22
25
|
export declare function sortFlows(nodeArray: SankeyNode[]): void;
|
|
23
26
|
interface LayoutOptions {
|
|
24
27
|
/** use node priority when sorting nodes vertically */
|
|
25
28
|
priority: boolean;
|
|
26
29
|
/** chart height in CSS pixels */
|
|
27
30
|
height: number;
|
|
28
|
-
/** vertical
|
|
29
|
-
nodePadding:
|
|
31
|
+
/** vertical before/after gap per node, in CSS pixels */
|
|
32
|
+
nodePadding: Map<string, NodeGap>;
|
|
33
|
+
/** how nodePadding gaps are distributed within a column, defaults to 'auto' */
|
|
34
|
+
nodePaddingMode: SankeyControllerDatasetOptions['nodePaddingMode'];
|
|
30
35
|
/** layout mode in x-direction */
|
|
31
36
|
modeX: SankeyControllerDatasetOptions['modeX'];
|
|
32
37
|
}
|
|
33
|
-
export declare function layout(nodes: Map<string, SankeyNode>, data: SankeyDataPoint[], { priority, height, nodePadding, modeX }: LayoutOptions): {
|
|
38
|
+
export declare function layout(nodes: Map<string, SankeyNode>, data: SankeyDataPoint[], { priority, height, nodePadding, nodePaddingMode, modeX }: LayoutOptions): {
|
|
34
39
|
maxY: number;
|
|
35
40
|
maxX: number;
|
|
36
41
|
};
|
package/dist/types.d.cts
CHANGED
|
@@ -12,15 +12,21 @@ export type SankeyScriptableContext = ScriptableContext<'sankey'> & {
|
|
|
12
12
|
export type SankeyLabelPosition = 'auto' | 'bottom' | 'center' | 'left' | 'right' | 'top';
|
|
13
13
|
export type SankeyNodeLabelPosition = SankeyLabelPosition;
|
|
14
14
|
export type SankeyOrientation = 'horizontal' | 'vertical';
|
|
15
|
-
export type
|
|
15
|
+
export type SankeyNodeOption<T> = T | Record<string, T> | ((node: SankeyNode) => T | undefined);
|
|
16
|
+
/** @deprecated use SankeyNodeOption */
|
|
17
|
+
export type SankeyNodeLabelOption<T> = SankeyNodeOption<T>;
|
|
18
|
+
export interface SankeyNodeGap {
|
|
19
|
+
after?: number;
|
|
20
|
+
before?: number;
|
|
21
|
+
}
|
|
16
22
|
export interface SankeyControllerDatasetNodeLabelsOptions {
|
|
17
|
-
backgroundColor?:
|
|
23
|
+
backgroundColor?: SankeyNodeOption<Color>;
|
|
18
24
|
borderRadius?: number;
|
|
19
|
-
color?:
|
|
20
|
-
display?:
|
|
25
|
+
color?: SankeyNodeOption<Color>;
|
|
26
|
+
display?: SankeyNodeOption<boolean>;
|
|
21
27
|
font?: Partial<FontSpec>;
|
|
22
28
|
padding?: number;
|
|
23
|
-
position?:
|
|
29
|
+
position?: SankeyNodeOption<SankeyLabelPosition>;
|
|
24
30
|
}
|
|
25
31
|
export interface SankeyControllerDatasetFlowLabelsOptions {
|
|
26
32
|
backgroundColor?: Scriptable<Color, SankeyScriptableContext>;
|
|
@@ -55,7 +61,8 @@ export interface SankeyControllerDatasetOptions extends Omit<ControllerDatasetOp
|
|
|
55
61
|
flowColor?: ScriptableAndArray<Color, SankeyScriptableContext>;
|
|
56
62
|
modeX?: 'edge' | 'even';
|
|
57
63
|
nodeLabels?: SankeyControllerDatasetNodeLabelsOptions;
|
|
58
|
-
nodePadding?: number
|
|
64
|
+
nodePadding?: SankeyNodeOption<number | SankeyNodeGap>;
|
|
65
|
+
nodePaddingMode?: 'auto' | 'even';
|
|
59
66
|
nodeWidth?: number;
|
|
60
67
|
orientation?: SankeyOrientation;
|
|
61
68
|
padding?: number;
|
package/dist/types.d.ts
CHANGED
|
@@ -11,15 +11,21 @@ export type SankeyScriptableContext = ScriptableContext<'sankey'> & {
|
|
|
11
11
|
export type SankeyLabelPosition = 'auto' | 'bottom' | 'center' | 'left' | 'right' | 'top';
|
|
12
12
|
export type SankeyNodeLabelPosition = SankeyLabelPosition;
|
|
13
13
|
export type SankeyOrientation = 'horizontal' | 'vertical';
|
|
14
|
-
export type
|
|
14
|
+
export type SankeyNodeOption<T> = T | Record<string, T> | ((node: SankeyNode) => T | undefined);
|
|
15
|
+
/** @deprecated use SankeyNodeOption */
|
|
16
|
+
export type SankeyNodeLabelOption<T> = SankeyNodeOption<T>;
|
|
17
|
+
export interface SankeyNodeGap {
|
|
18
|
+
after?: number;
|
|
19
|
+
before?: number;
|
|
20
|
+
}
|
|
15
21
|
export interface SankeyControllerDatasetNodeLabelsOptions {
|
|
16
|
-
backgroundColor?:
|
|
22
|
+
backgroundColor?: SankeyNodeOption<Color>;
|
|
17
23
|
borderRadius?: number;
|
|
18
|
-
color?:
|
|
19
|
-
display?:
|
|
24
|
+
color?: SankeyNodeOption<Color>;
|
|
25
|
+
display?: SankeyNodeOption<boolean>;
|
|
20
26
|
font?: Partial<FontSpec>;
|
|
21
27
|
padding?: number;
|
|
22
|
-
position?:
|
|
28
|
+
position?: SankeyNodeOption<SankeyLabelPosition>;
|
|
23
29
|
}
|
|
24
30
|
export interface SankeyControllerDatasetFlowLabelsOptions {
|
|
25
31
|
backgroundColor?: Scriptable<Color, SankeyScriptableContext>;
|
|
@@ -54,7 +60,8 @@ export interface SankeyControllerDatasetOptions extends Omit<ControllerDatasetOp
|
|
|
54
60
|
flowColor?: ScriptableAndArray<Color, SankeyScriptableContext>;
|
|
55
61
|
modeX?: 'edge' | 'even';
|
|
56
62
|
nodeLabels?: SankeyControllerDatasetNodeLabelsOptions;
|
|
57
|
-
nodePadding?: number
|
|
63
|
+
nodePadding?: SankeyNodeOption<number | SankeyNodeGap>;
|
|
64
|
+
nodePaddingMode?: 'auto' | 'even';
|
|
58
65
|
nodeWidth?: number;
|
|
59
66
|
orientation?: SankeyOrientation;
|
|
60
67
|
padding?: number;
|
package/package.json
CHANGED