chartjs-chart-treemap 5.0.0-next.8 → 5.0.0-next.9
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-treemap.cjs +466 -319
- package/dist/chartjs-chart-treemap.esm.js +467 -320
- package/dist/chartjs-chart-treemap.min.js +2 -2
- package/dist/chartjs-chart-treemap.min.js.map +1 -1
- package/dist/controller.d.ts +14 -0
- package/dist/layout.d.ts +3 -1
- package/dist/squarify.d.ts +7 -2
- package/dist/text/draw.d.ts +30 -0
- package/dist/text/fit.d.ts +9 -0
- package/dist/{text.d.ts → text/index.d.ts} +2 -2
- package/dist/text/measure.d.ts +26 -0
- package/dist/text/wrap.d.ts +6 -0
- package/dist/types.d.ts +27 -0
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* chartjs-chart-treemap v5.0.0-next.
|
|
2
|
+
* chartjs-chart-treemap v5.0.0-next.9
|
|
3
3
|
* https://chartjs-chart-treemap.pages.dev/
|
|
4
4
|
* (c) 2026 Jukka Kurkela
|
|
5
5
|
* Released under the MIT license
|
|
@@ -10,7 +10,7 @@ typeof define === 'function' && define.amd ? define(['exports', 'chart.js', 'cha
|
|
|
10
10
|
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["chartjs-chart-treemap"] = {}, global.Chart, global.Chart.helpers));
|
|
11
11
|
})(this, (function (exports, chart_js, helpers) { 'use strict';
|
|
12
12
|
|
|
13
|
-
var version = "5.0.0-next.
|
|
13
|
+
var version = "5.0.0-next.9";
|
|
14
14
|
|
|
15
15
|
function scaleRect(sq, xScale, yScale, sp) {
|
|
16
16
|
const sp2 = sp * 2;
|
|
@@ -60,6 +60,14 @@ function parseBorderRadius(value, maxW, maxH) {
|
|
|
60
60
|
|
|
61
61
|
function getDims(itm, w2, s2, key) {
|
|
62
62
|
const a = itm._normalized;
|
|
63
|
+
if (!a) {
|
|
64
|
+
return {
|
|
65
|
+
d1: 0,
|
|
66
|
+
d2: 0,
|
|
67
|
+
h: 0,
|
|
68
|
+
w: 0
|
|
69
|
+
};
|
|
70
|
+
}
|
|
63
71
|
const ar = w2 * a / s2;
|
|
64
72
|
const d1 = Math.sqrt(a * ar);
|
|
65
73
|
const d2 = a / d1;
|
|
@@ -427,6 +435,7 @@ function requireVersion(pkg, min, ver, strict = true) {
|
|
|
427
435
|
return true;
|
|
428
436
|
}
|
|
429
437
|
|
|
438
|
+
const WEIGHT_KEY = '_weight';
|
|
430
439
|
function compareAspectRatio(oldStat, newStat, args) {
|
|
431
440
|
if (oldStat.sum === 0) {
|
|
432
441
|
return true;
|
|
@@ -460,16 +469,17 @@ function compareAspectRatio(oldStat, newStat, args) {
|
|
|
460
469
|
return node;
|
|
461
470
|
});
|
|
462
471
|
}
|
|
463
|
-
function sortNodes(nodes) {
|
|
464
|
-
sort(nodes, 'v');
|
|
472
|
+
function sortNodes(nodes, weighted = false) {
|
|
473
|
+
sort(nodes, weighted ? WEIGHT_KEY : 'v');
|
|
465
474
|
}
|
|
466
|
-
function packInto(nodes, rectangle) {
|
|
475
|
+
function packInto(nodes, rectangle, weighted = false) {
|
|
467
476
|
const n = nodes.length;
|
|
468
477
|
if (!n) {
|
|
469
478
|
return;
|
|
470
479
|
}
|
|
480
|
+
const key = weighted ? WEIGHT_KEY : 'v';
|
|
471
481
|
const rect = new Rect(rectangle);
|
|
472
|
-
const row = new StatArray(
|
|
482
|
+
const row = new StatArray(key, rect.area / sum(nodes, key));
|
|
473
483
|
let length = rect.side;
|
|
474
484
|
let o;
|
|
475
485
|
for(let i = 0; i < n; ++i){
|
|
@@ -488,47 +498,66 @@ function compareAspectRatio(oldStat, newStat, args) {
|
|
|
488
498
|
|
|
489
499
|
const WIDTH_CACHE_LIMIT = 4096;
|
|
490
500
|
const widthCache = new Map();
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
501
|
+
const fontFor = (fonts, index)=>fonts[Math.min(index, fonts.length - 1)];
|
|
502
|
+
function measureLabelSize(ctx, lines, fonts) {
|
|
503
|
+
const fontsKey = fonts.reduce((prev, item)=>{
|
|
504
|
+
prev += item.string;
|
|
505
|
+
return prev;
|
|
506
|
+
}, '');
|
|
507
|
+
const mapKey = lines.join() + fontsKey + (ctx._measureText ? '-spriting' : '');
|
|
508
|
+
let size = widthCache.get(mapKey);
|
|
509
|
+
if (!size) {
|
|
510
|
+
ctx.save();
|
|
511
|
+
const count = lines.length;
|
|
512
|
+
let width = 0;
|
|
513
|
+
let height = 0;
|
|
514
|
+
for(let i = 0; i < count; i++){
|
|
515
|
+
const font = fonts[Math.min(i, fonts.length - 1)];
|
|
516
|
+
ctx.font = font.string;
|
|
517
|
+
const text = lines[i];
|
|
518
|
+
width = Math.max(width, ctx.measureText(text).width);
|
|
519
|
+
height += font.lineHeight;
|
|
520
|
+
}
|
|
521
|
+
ctx.restore();
|
|
522
|
+
size = {
|
|
523
|
+
height,
|
|
524
|
+
width
|
|
525
|
+
};
|
|
526
|
+
if (widthCache.size >= WIDTH_CACHE_LIMIT) {
|
|
527
|
+
widthCache.clear();
|
|
528
|
+
}
|
|
529
|
+
widthCache.set(mapKey, size);
|
|
497
530
|
}
|
|
498
|
-
|
|
499
|
-
const font = helpers.toFont(options.font || {});
|
|
500
|
-
const min = font.lineHeight;
|
|
501
|
-
const padding = Math.max(Math.min(helpers.valueOrDefault(options.padding, 3) * 2, Math.min(w, h)), 0);
|
|
502
|
-
return w - padding > min && h - padding > min;
|
|
531
|
+
return size;
|
|
503
532
|
}
|
|
504
|
-
function
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
533
|
+
function measureLines(ctx, lines, styles, fonts) {
|
|
534
|
+
let width = 0;
|
|
535
|
+
let height = 0;
|
|
536
|
+
lines.forEach((line, i)=>{
|
|
537
|
+
const font = fontFor(fonts, styles[i]);
|
|
538
|
+
width = Math.max(width, measureLabelSize(ctx, [
|
|
539
|
+
line
|
|
540
|
+
], [
|
|
541
|
+
font
|
|
542
|
+
]).width);
|
|
543
|
+
height += font.lineHeight;
|
|
544
|
+
});
|
|
545
|
+
return {
|
|
546
|
+
height,
|
|
547
|
+
width
|
|
548
|
+
};
|
|
510
549
|
}
|
|
511
|
-
function
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
}
|
|
520
|
-
} else if (shouldDrawCaption(displayMode, rect, captions)) {
|
|
521
|
-
block = captionBlock(ctx, rect, options, item, element, layout);
|
|
522
|
-
}
|
|
523
|
-
if (!block) {
|
|
524
|
-
return;
|
|
525
|
-
}
|
|
526
|
-
ctx.save();
|
|
527
|
-
clipToPadding(ctx, rect, block.padding, block.clipY);
|
|
528
|
-
drawTextBlock(ctx, rect, block);
|
|
529
|
-
ctx.restore();
|
|
550
|
+
function toFonts(fonts, fitRatio) {
|
|
551
|
+
return fonts.map((f)=>{
|
|
552
|
+
const { lineHeight: _lineHeight, ...font } = f;
|
|
553
|
+
return helpers.toFont({
|
|
554
|
+
...font,
|
|
555
|
+
size: Math.floor(f.size * fitRatio)
|
|
556
|
+
});
|
|
557
|
+
});
|
|
530
558
|
}
|
|
531
|
-
|
|
559
|
+
|
|
560
|
+
function clipToPadding(ctx, rect, padding, clipY) {
|
|
532
561
|
const x = Math.max(0, padding);
|
|
533
562
|
const y = Math.max(0, clipY);
|
|
534
563
|
ctx.beginPath();
|
|
@@ -551,134 +580,45 @@ function drawTextBlock(ctx, rect, block) {
|
|
|
551
580
|
offset += font.lineHeight;
|
|
552
581
|
});
|
|
553
582
|
}
|
|
554
|
-
function
|
|
555
|
-
const
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
type: 'data'
|
|
559
|
-
};
|
|
560
|
-
context.active = !!element.active;
|
|
561
|
-
context.raw = item;
|
|
562
|
-
return context;
|
|
563
|
-
}
|
|
564
|
-
function resolveOption(option, context) {
|
|
565
|
-
return typeof option === 'function' ? option(context) : option;
|
|
566
|
-
}
|
|
567
|
-
function resolveCaptionText(captions, element, item) {
|
|
568
|
-
return resolveOption(captions.formatter, callbackContext(element, item)) || item.g || '';
|
|
569
|
-
}
|
|
570
|
-
function captionText(ctx, rect, captions, element, item, fonts) {
|
|
571
|
-
const text = resolveCaptionText(captions, element, item);
|
|
572
|
-
if (!text) {
|
|
573
|
-
return undefined;
|
|
574
|
-
}
|
|
575
|
-
const padding = captions.padding;
|
|
576
|
-
if (measureLabelSize(ctx, [
|
|
577
|
-
text
|
|
578
|
-
], fonts).width + 2 * padding <= rect.w) {
|
|
579
|
-
return text;
|
|
580
|
-
}
|
|
581
|
-
const overflow = captions.overflow || 'ellipsis';
|
|
582
|
-
if (overflow === 'hidden') {
|
|
583
|
-
return undefined;
|
|
583
|
+
function calculateBlockTop(rect, options, labelSize) {
|
|
584
|
+
const { position, padding } = options;
|
|
585
|
+
if (position === 'top') {
|
|
586
|
+
return rect.y + padding;
|
|
584
587
|
}
|
|
585
|
-
if (
|
|
586
|
-
return
|
|
588
|
+
if (position === 'bottom') {
|
|
589
|
+
return rect.y + rect.h - padding - labelSize.height;
|
|
587
590
|
}
|
|
588
|
-
return
|
|
591
|
+
return rect.y + (rect.h - labelSize.height) / 2 + padding;
|
|
589
592
|
}
|
|
590
|
-
function
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
const { color, hoverColor, font, hoverFont, padding, align } = captions;
|
|
594
|
-
const optFont = (rect.active ? hoverFont : font) || font;
|
|
595
|
-
const oFont = helpers.toFont(optFont);
|
|
596
|
-
if (oFont.lineHeight > rect.h) {
|
|
597
|
-
return;
|
|
593
|
+
function calculateBlockLeft(rect, align, padding, width) {
|
|
594
|
+
if (align === 'left') {
|
|
595
|
+
return rect.x + padding;
|
|
598
596
|
}
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
]);
|
|
602
|
-
if (text === undefined) {
|
|
603
|
-
return;
|
|
597
|
+
if (align === 'right') {
|
|
598
|
+
return rect.x + rect.w - padding - width;
|
|
604
599
|
}
|
|
605
|
-
|
|
606
|
-
oFont
|
|
607
|
-
];
|
|
608
|
-
const top = displayMode === 'headerBoxes' ? rect.y + (rect.h - oFont.lineHeight) / 2 : rect.y + padding + spacing;
|
|
609
|
-
return {
|
|
610
|
-
align: align || (rtl ? 'right' : 'left'),
|
|
611
|
-
clipY: displayMode === 'headerBoxes' ? 0 : padding,
|
|
612
|
-
colors: [
|
|
613
|
-
(rect.active ? hoverColor : color) || color
|
|
614
|
-
],
|
|
615
|
-
fonts,
|
|
616
|
-
lines: [
|
|
617
|
-
text
|
|
618
|
-
],
|
|
619
|
-
padding,
|
|
620
|
-
styles: [
|
|
621
|
-
0
|
|
622
|
-
],
|
|
623
|
-
textAlign: captions.textAlign,
|
|
624
|
-
top,
|
|
625
|
-
width: measureLabelSize(ctx, [
|
|
626
|
-
text
|
|
627
|
-
], fonts).width
|
|
628
|
-
};
|
|
600
|
+
return rect.x + (rect.w - width) / 2;
|
|
629
601
|
}
|
|
630
|
-
function
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
const maxWidth = rect.w - padding * 2;
|
|
638
|
-
const maxHeight = rect.h - padding * 2;
|
|
639
|
-
let fonts = getFontFromOptions(rect, labels);
|
|
640
|
-
let { lines, styles } = toLines(ctx, content, fonts, maxWidth, !!wrap);
|
|
641
|
-
let labelSize = measureLines(ctx, lines, styles, fonts);
|
|
642
|
-
const lblToDraw = labelToDraw(rect, labels, labelSize);
|
|
643
|
-
if (!lblToDraw) {
|
|
644
|
-
return;
|
|
645
|
-
}
|
|
646
|
-
if (helpers.isNumber(lblToDraw)) {
|
|
647
|
-
fonts = toFonts(fonts, lblToDraw);
|
|
648
|
-
if (wrap) {
|
|
649
|
-
const rewrapped = toLines(ctx, content, fonts, maxWidth, true);
|
|
650
|
-
lines = rewrapped.lines;
|
|
651
|
-
styles = rewrapped.styles;
|
|
652
|
-
labelSize = measureLines(ctx, lines, styles, fonts);
|
|
653
|
-
} else {
|
|
654
|
-
labelSize = {
|
|
655
|
-
height: labelSize.height * lblToDraw,
|
|
656
|
-
width: labelSize.width * lblToDraw
|
|
657
|
-
};
|
|
602
|
+
function calculateLineX(rect, align, lineAlign, padding, width) {
|
|
603
|
+
if (align === lineAlign) {
|
|
604
|
+
if (align === 'left') {
|
|
605
|
+
return rect.x + padding;
|
|
606
|
+
}
|
|
607
|
+
if (align === 'right') {
|
|
608
|
+
return rect.x + rect.w - padding;
|
|
658
609
|
}
|
|
610
|
+
return rect.x + rect.w / 2;
|
|
659
611
|
}
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
styles = trimmed.styles;
|
|
664
|
-
labelSize = measureLines(ctx, lines, styles, fonts);
|
|
612
|
+
const left = calculateBlockLeft(rect, align, padding, width);
|
|
613
|
+
if (lineAlign === 'left') {
|
|
614
|
+
return left;
|
|
665
615
|
}
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
colors: helpers.isArray(optColor) ? optColor : [
|
|
671
|
-
optColor
|
|
672
|
-
],
|
|
673
|
-
fonts,
|
|
674
|
-
lines,
|
|
675
|
-
padding,
|
|
676
|
-
styles,
|
|
677
|
-
textAlign: labels.textAlign,
|
|
678
|
-
top: calculateBlockTop(rect, labels, labelSize),
|
|
679
|
-
width: labelSize.width
|
|
680
|
-
};
|
|
616
|
+
if (lineAlign === 'right') {
|
|
617
|
+
return left + width;
|
|
618
|
+
}
|
|
619
|
+
return left + width / 2;
|
|
681
620
|
}
|
|
621
|
+
|
|
682
622
|
function sliceTextToFitWidth(ctx, text, width, fonts) {
|
|
683
623
|
const ellipsis = '...';
|
|
684
624
|
const ellipsisWidth = measureLabelSize(ctx, [
|
|
@@ -705,14 +645,60 @@ function sliceTextToFitWidth(ctx, text, width, fonts) {
|
|
|
705
645
|
const slicedText = text.slice(0, Math.max(0, lowerBoundLen - 1));
|
|
706
646
|
return slicedText ? slicedText + ellipsis : '';
|
|
707
647
|
}
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
];
|
|
648
|
+
function applyEllipsis(ctx, lines, styles, fonts, maxWidth, maxHeight) {
|
|
649
|
+
const keptLines = [];
|
|
650
|
+
const keptStyles = [];
|
|
651
|
+
let used = 0;
|
|
652
|
+
for(let i = 0; i < lines.length; i++){
|
|
653
|
+
const font = fontFor(fonts, styles[i]);
|
|
654
|
+
if (keptLines.length && used + font.lineHeight > maxHeight) {
|
|
655
|
+
break;
|
|
656
|
+
}
|
|
657
|
+
used += font.lineHeight;
|
|
658
|
+
keptLines.push(lines[i]);
|
|
659
|
+
keptStyles.push(styles[i]);
|
|
714
660
|
}
|
|
715
|
-
const
|
|
661
|
+
const dropped = keptLines.length < lines.length;
|
|
662
|
+
const last = keptLines.length - 1;
|
|
663
|
+
for(let i = 0; i < keptLines.length; i++){
|
|
664
|
+
const font = fontFor(fonts, keptStyles[i]);
|
|
665
|
+
const tooWide = measureLabelSize(ctx, [
|
|
666
|
+
keptLines[i]
|
|
667
|
+
], [
|
|
668
|
+
font
|
|
669
|
+
]).width > maxWidth;
|
|
670
|
+
if (tooWide || dropped && i === last) {
|
|
671
|
+
keptLines[i] = sliceTextToFitWidth(ctx, keptLines[i], maxWidth, [
|
|
672
|
+
font
|
|
673
|
+
]);
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
return {
|
|
677
|
+
lines: keptLines,
|
|
678
|
+
styles: keptStyles
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
function labelToDraw(rect, options, labelSize) {
|
|
682
|
+
const { overflow, padding } = options;
|
|
683
|
+
const { width, height } = labelSize;
|
|
684
|
+
if (overflow === 'hidden') {
|
|
685
|
+
return !(width + padding * 2 > rect.w || height + padding * 2 > rect.h);
|
|
686
|
+
} else if (overflow === 'fit') {
|
|
687
|
+
const ratio = Math.min(rect.w / (width + padding * 2), rect.h / (height + padding * 2));
|
|
688
|
+
if (ratio < 1) {
|
|
689
|
+
return ratio;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
return true;
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
function wrapToWidth(ctx, text, font, width) {
|
|
696
|
+
if (width <= 0) {
|
|
697
|
+
return [
|
|
698
|
+
text
|
|
699
|
+
];
|
|
700
|
+
}
|
|
701
|
+
const fits = (value)=>measureLabelSize(ctx, [
|
|
716
702
|
value
|
|
717
703
|
], [
|
|
718
704
|
font
|
|
@@ -753,7 +739,7 @@ const fontFor = (fonts, index)=>fonts[Math.min(index, fonts.length - 1)];
|
|
|
753
739
|
''
|
|
754
740
|
];
|
|
755
741
|
}
|
|
756
|
-
|
|
742
|
+
function toLines(ctx, content, fonts, width, wrap) {
|
|
757
743
|
const paragraphs = (helpers.isArray(content) ? content : [
|
|
758
744
|
content
|
|
759
745
|
]).map((entry)=>`${entry}`);
|
|
@@ -775,168 +761,193 @@ const fontFor = (fonts, index)=>fonts[Math.min(index, fonts.length - 1)];
|
|
|
775
761
|
styles
|
|
776
762
|
};
|
|
777
763
|
}
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
return
|
|
791
|
-
height,
|
|
792
|
-
width
|
|
793
|
-
};
|
|
764
|
+
|
|
765
|
+
function shouldDrawCaption(displayMode, rect, options) {
|
|
766
|
+
if (!options || options.display === false) {
|
|
767
|
+
return false;
|
|
768
|
+
}
|
|
769
|
+
if (displayMode === 'headerBoxes') {
|
|
770
|
+
return true;
|
|
771
|
+
}
|
|
772
|
+
const { w, h } = rect;
|
|
773
|
+
const font = helpers.toFont(options.font || {});
|
|
774
|
+
const min = font.lineHeight;
|
|
775
|
+
const padding = Math.max(Math.min(helpers.valueOrDefault(options.padding, 3) * 2, Math.min(w, h)), 0);
|
|
776
|
+
return w - padding > min && h - padding > min;
|
|
794
777
|
}
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
let used = 0;
|
|
799
|
-
for(let i = 0; i < lines.length; i++){
|
|
800
|
-
const font = fontFor(fonts, styles[i]);
|
|
801
|
-
if (keptLines.length && used + font.lineHeight > maxHeight) {
|
|
802
|
-
break;
|
|
803
|
-
}
|
|
804
|
-
used += font.lineHeight;
|
|
805
|
-
keptLines.push(lines[i]);
|
|
806
|
-
keptStyles.push(styles[i]);
|
|
778
|
+
function getCaptionHeight(displayMode, rect, font, padding) {
|
|
779
|
+
if (displayMode !== 'headerBoxes') {
|
|
780
|
+
return font.lineHeight + padding * 2;
|
|
807
781
|
}
|
|
808
|
-
const
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
if (
|
|
818
|
-
|
|
819
|
-
font
|
|
820
|
-
]);
|
|
782
|
+
const captionHeight = font.lineHeight + padding * 2;
|
|
783
|
+
return rect.h < 2 * captionHeight ? rect.h / 3 : captionHeight;
|
|
784
|
+
}
|
|
785
|
+
function drawText(ctx, rect, options, item, element, layout) {
|
|
786
|
+
const { captions, labels } = options;
|
|
787
|
+
const { displayMode } = layout;
|
|
788
|
+
const isLeaf = item && (!helpers.defined(item.l) || item.isLeaf);
|
|
789
|
+
let block;
|
|
790
|
+
if (isLeaf) {
|
|
791
|
+
if (labels.display) {
|
|
792
|
+
block = labelBlock(ctx, rect, options, item, element);
|
|
821
793
|
}
|
|
794
|
+
} else if (shouldDrawCaption(displayMode, rect, captions)) {
|
|
795
|
+
block = captionBlock(ctx, rect, options, item, element, layout);
|
|
822
796
|
}
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
797
|
+
if (!block) {
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
ctx.save();
|
|
801
|
+
clipToPadding(ctx, rect, block.padding, block.clipY);
|
|
802
|
+
drawTextBlock(ctx, rect, block);
|
|
803
|
+
ctx.restore();
|
|
804
|
+
}
|
|
805
|
+
function callbackContext(element, item) {
|
|
806
|
+
const context = element.$context || {
|
|
807
|
+
active: !!element.active,
|
|
808
|
+
raw: item,
|
|
809
|
+
type: 'data'
|
|
826
810
|
};
|
|
811
|
+
context.active = !!element.active;
|
|
812
|
+
context.raw = item;
|
|
813
|
+
return context;
|
|
827
814
|
}
|
|
828
|
-
function
|
|
829
|
-
|
|
830
|
-
prev += item.string;
|
|
831
|
-
return prev;
|
|
832
|
-
}, '');
|
|
833
|
-
const mapKey = lines.join() + fontsKey + (ctx._measureText ? '-spriting' : '');
|
|
834
|
-
let size = widthCache.get(mapKey);
|
|
835
|
-
if (!size) {
|
|
836
|
-
ctx.save();
|
|
837
|
-
const count = lines.length;
|
|
838
|
-
let width = 0;
|
|
839
|
-
let height = 0;
|
|
840
|
-
for(let i = 0; i < count; i++){
|
|
841
|
-
const font = fonts[Math.min(i, fonts.length - 1)];
|
|
842
|
-
ctx.font = font.string;
|
|
843
|
-
const text = lines[i];
|
|
844
|
-
width = Math.max(width, ctx.measureText(text).width);
|
|
845
|
-
height += font.lineHeight;
|
|
846
|
-
}
|
|
847
|
-
ctx.restore();
|
|
848
|
-
size = {
|
|
849
|
-
height,
|
|
850
|
-
width
|
|
851
|
-
};
|
|
852
|
-
if (widthCache.size >= WIDTH_CACHE_LIMIT) {
|
|
853
|
-
widthCache.clear();
|
|
854
|
-
}
|
|
855
|
-
widthCache.set(mapKey, size);
|
|
856
|
-
}
|
|
857
|
-
return size;
|
|
815
|
+
function resolveOption(option, context) {
|
|
816
|
+
return typeof option === 'function' ? option(context) : option;
|
|
858
817
|
}
|
|
859
|
-
function
|
|
860
|
-
return
|
|
861
|
-
const { lineHeight: _lineHeight, ...font } = f;
|
|
862
|
-
return helpers.toFont({
|
|
863
|
-
...font,
|
|
864
|
-
size: Math.floor(f.size * fitRatio)
|
|
865
|
-
});
|
|
866
|
-
});
|
|
818
|
+
function resolveCaptionText(captions, element, item) {
|
|
819
|
+
return resolveOption(captions.formatter, callbackContext(element, item)) || item.g || '';
|
|
867
820
|
}
|
|
868
|
-
function
|
|
869
|
-
const
|
|
870
|
-
|
|
821
|
+
function captionText(ctx, rect, captions, element, item, fonts) {
|
|
822
|
+
const text = resolveCaptionText(captions, element, item);
|
|
823
|
+
if (!text) {
|
|
824
|
+
return undefined;
|
|
825
|
+
}
|
|
826
|
+
const padding = captions.padding;
|
|
827
|
+
if (measureLabelSize(ctx, [
|
|
828
|
+
text
|
|
829
|
+
], fonts).width + 2 * padding <= rect.w) {
|
|
830
|
+
return text;
|
|
831
|
+
}
|
|
832
|
+
const overflow = captions.overflow || 'ellipsis';
|
|
871
833
|
if (overflow === 'hidden') {
|
|
872
|
-
return
|
|
873
|
-
} else if (overflow === 'fit') {
|
|
874
|
-
const ratio = Math.min(rect.w / (width + padding * 2), rect.h / (height + padding * 2));
|
|
875
|
-
if (ratio < 1) {
|
|
876
|
-
return ratio;
|
|
877
|
-
}
|
|
834
|
+
return undefined;
|
|
878
835
|
}
|
|
879
|
-
|
|
836
|
+
if (overflow === 'cut') {
|
|
837
|
+
return text;
|
|
838
|
+
}
|
|
839
|
+
return sliceTextToFitWidth(ctx, text, rect.w - 2 * padding, fonts);
|
|
880
840
|
}
|
|
881
|
-
function
|
|
882
|
-
const {
|
|
841
|
+
function captionBlock(ctx, rect, options, item, element, layout) {
|
|
842
|
+
const { displayMode, rtl, spacing } = layout;
|
|
843
|
+
const { captions } = options;
|
|
844
|
+
const { color, hoverColor, font, hoverFont, padding, align } = captions;
|
|
883
845
|
const optFont = (rect.active ? hoverFont : font) || font;
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
}
|
|
888
|
-
function calculateBlockTop(rect, options, labelSize) {
|
|
889
|
-
const { position, padding } = options;
|
|
890
|
-
if (position === 'top') {
|
|
891
|
-
return rect.y + padding;
|
|
846
|
+
const oFont = helpers.toFont(optFont);
|
|
847
|
+
if (oFont.lineHeight > rect.h) {
|
|
848
|
+
return;
|
|
892
849
|
}
|
|
893
|
-
|
|
894
|
-
|
|
850
|
+
const text = captionText(ctx, rect, captions, element, item, [
|
|
851
|
+
oFont
|
|
852
|
+
]);
|
|
853
|
+
if (text === undefined) {
|
|
854
|
+
return;
|
|
895
855
|
}
|
|
896
|
-
|
|
856
|
+
const fonts = [
|
|
857
|
+
oFont
|
|
858
|
+
];
|
|
859
|
+
const top = displayMode === 'headerBoxes' ? rect.y + (rect.h - oFont.lineHeight) / 2 : rect.y + padding + spacing;
|
|
860
|
+
return {
|
|
861
|
+
align: align || (rtl ? 'right' : 'left'),
|
|
862
|
+
clipY: displayMode === 'headerBoxes' ? 0 : padding,
|
|
863
|
+
colors: [
|
|
864
|
+
(rect.active ? hoverColor : color) || color
|
|
865
|
+
],
|
|
866
|
+
fonts,
|
|
867
|
+
lines: [
|
|
868
|
+
text
|
|
869
|
+
],
|
|
870
|
+
padding,
|
|
871
|
+
styles: [
|
|
872
|
+
0
|
|
873
|
+
],
|
|
874
|
+
textAlign: captions.textAlign,
|
|
875
|
+
top,
|
|
876
|
+
width: measureLabelSize(ctx, [
|
|
877
|
+
text
|
|
878
|
+
], fonts).width
|
|
879
|
+
};
|
|
897
880
|
}
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
881
|
+
function labelBlock(ctx, rect, options, item, element) {
|
|
882
|
+
const labels = options.labels;
|
|
883
|
+
const content = resolveOption(labels.formatter, callbackContext(element, item));
|
|
884
|
+
if (!content) {
|
|
885
|
+
return;
|
|
901
886
|
}
|
|
902
|
-
|
|
903
|
-
|
|
887
|
+
const { align, color, hoverColor, overflow, padding, wrap } = labels;
|
|
888
|
+
const maxWidth = rect.w - padding * 2;
|
|
889
|
+
const maxHeight = rect.h - padding * 2;
|
|
890
|
+
let fonts = getFontFromOptions(rect, labels);
|
|
891
|
+
let { lines, styles } = toLines(ctx, content, fonts, maxWidth, !!wrap);
|
|
892
|
+
let labelSize = measureLines(ctx, lines, styles, fonts);
|
|
893
|
+
const lblToDraw = labelToDraw(rect, labels, labelSize);
|
|
894
|
+
if (!lblToDraw) {
|
|
895
|
+
return;
|
|
904
896
|
}
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
897
|
+
if (helpers.isNumber(lblToDraw)) {
|
|
898
|
+
fonts = toFonts(fonts, lblToDraw);
|
|
899
|
+
if (wrap) {
|
|
900
|
+
const rewrapped = toLines(ctx, content, fonts, maxWidth, true);
|
|
901
|
+
lines = rewrapped.lines;
|
|
902
|
+
styles = rewrapped.styles;
|
|
903
|
+
labelSize = measureLines(ctx, lines, styles, fonts);
|
|
904
|
+
} else {
|
|
905
|
+
labelSize = {
|
|
906
|
+
height: labelSize.height * lblToDraw,
|
|
907
|
+
width: labelSize.width * lblToDraw
|
|
908
|
+
};
|
|
914
909
|
}
|
|
915
|
-
return rect.x + rect.w / 2;
|
|
916
910
|
}
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
return left + width;
|
|
911
|
+
if (overflow === 'ellipsis') {
|
|
912
|
+
const trimmed = applyEllipsis(ctx, lines, styles, fonts, maxWidth, maxHeight);
|
|
913
|
+
lines = trimmed.lines;
|
|
914
|
+
styles = trimmed.styles;
|
|
915
|
+
labelSize = measureLines(ctx, lines, styles, fonts);
|
|
923
916
|
}
|
|
924
|
-
|
|
917
|
+
const optColor = (rect.active ? hoverColor : color) || color;
|
|
918
|
+
return {
|
|
919
|
+
align,
|
|
920
|
+
clipY: padding,
|
|
921
|
+
colors: helpers.isArray(optColor) ? optColor : [
|
|
922
|
+
optColor
|
|
923
|
+
],
|
|
924
|
+
fonts,
|
|
925
|
+
lines,
|
|
926
|
+
padding,
|
|
927
|
+
styles,
|
|
928
|
+
textAlign: labels.textAlign,
|
|
929
|
+
top: calculateBlockTop(rect, labels, labelSize),
|
|
930
|
+
width: labelSize.width
|
|
931
|
+
};
|
|
932
|
+
}
|
|
933
|
+
function getFontFromOptions(rect, labels) {
|
|
934
|
+
const { font, hoverFont } = labels;
|
|
935
|
+
const optFont = (rect.active ? hoverFont : font) || font;
|
|
936
|
+
return Array.isArray(optFont) ? optFont.map((f)=>helpers.toFont(f)) : [
|
|
937
|
+
helpers.toFont(optFont)
|
|
938
|
+
];
|
|
925
939
|
}
|
|
926
940
|
|
|
927
|
-
function buildHierarchy(values, gidx, keys, layout, parentGroupValue) {
|
|
941
|
+
function buildHierarchy(values, gidx, keys, layout, weighted, parentGroupValue) {
|
|
928
942
|
const { groups, leafKey, unsorted } = layout;
|
|
929
943
|
const glen = groups.length;
|
|
930
944
|
const g = getGroupKey(groups[gidx]);
|
|
931
945
|
const gdata = group(values, g, keys, leafKey, undefined, parentGroupValue, groups.filter((_item, index)=>index <= gidx), groups, gidx);
|
|
932
946
|
const nodes = toNodes(gdata, keys, g, gidx);
|
|
933
|
-
if (!unsorted) {
|
|
934
|
-
sortNodes(nodes);
|
|
935
|
-
}
|
|
936
947
|
for (const node of nodes){
|
|
937
948
|
const record = node._data;
|
|
938
949
|
const nextGroupIndex = record.groupIndex + 1;
|
|
939
|
-
const children = gidx < glen - 1 && nextGroupIndex < glen ? buildHierarchy(record.children, nextGroupIndex, keys, layout, node.g) : [];
|
|
950
|
+
const children = gidx < glen - 1 && nextGroupIndex < glen ? buildHierarchy(record.children, nextGroupIndex, keys, layout, weighted, node.g) : [];
|
|
940
951
|
Object.defineProperty(node, '_children', {
|
|
941
952
|
configurable: true,
|
|
942
953
|
value: children,
|
|
@@ -944,8 +955,91 @@ function buildHierarchy(values, gidx, keys, layout, parentGroupValue) {
|
|
|
944
955
|
});
|
|
945
956
|
node.isLeaf = !children.length;
|
|
946
957
|
}
|
|
947
|
-
|
|
958
|
+
if (weighted) {
|
|
959
|
+
applyValueScale(nodes, scaleFor(layout));
|
|
960
|
+
}
|
|
961
|
+
const withOthers = layout.others ? applyOthers(nodes, layout.others, weighted) : nodes;
|
|
962
|
+
if (!unsorted) {
|
|
963
|
+
sortNodes(withOthers, weighted);
|
|
964
|
+
}
|
|
965
|
+
return withOthers;
|
|
966
|
+
}
|
|
967
|
+
function applyValueScale(nodes, scale) {
|
|
968
|
+
for (const node of nodes){
|
|
969
|
+
const children = node._children;
|
|
970
|
+
let weight;
|
|
971
|
+
if (children?.length) {
|
|
972
|
+
applyValueScale(children, scale);
|
|
973
|
+
weight = children.reduce((total, child)=>total + child[WEIGHT_KEY], 0);
|
|
974
|
+
} else {
|
|
975
|
+
const scaled = scale(node.v, node._data);
|
|
976
|
+
weight = Number.isFinite(scaled) && scaled > 0 ? scaled : 0;
|
|
977
|
+
}
|
|
978
|
+
Object.defineProperty(node, WEIGHT_KEY, {
|
|
979
|
+
configurable: true,
|
|
980
|
+
value: weight,
|
|
981
|
+
writable: true
|
|
982
|
+
});
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
function applyOthers(nodes, others, weighted) {
|
|
986
|
+
const { label = 'Other', minCount = 2, threshold } = others;
|
|
987
|
+
if (!(threshold > 0) || nodes.some((node)=>node._children?.length)) {
|
|
988
|
+
return nodes;
|
|
989
|
+
}
|
|
990
|
+
const weightOf = (node)=>weighted ? node[WEIGHT_KEY] : node.v;
|
|
991
|
+
const total = nodes.reduce((sum, node)=>sum + weightOf(node), 0);
|
|
992
|
+
if (!(total > 0)) {
|
|
993
|
+
return nodes;
|
|
994
|
+
}
|
|
995
|
+
const small = nodes.filter((node)=>weightOf(node) / total < threshold);
|
|
996
|
+
if (small.length < Math.max(2, minCount)) {
|
|
997
|
+
return nodes;
|
|
998
|
+
}
|
|
999
|
+
const kept = nodes.filter((node)=>!small.includes(node));
|
|
1000
|
+
const bucket = {
|
|
1001
|
+
_data: {
|
|
1002
|
+
label,
|
|
1003
|
+
others: small.map((node)=>node._data)
|
|
1004
|
+
},
|
|
1005
|
+
h: 0,
|
|
1006
|
+
isLeaf: true,
|
|
1007
|
+
isOthers: true,
|
|
1008
|
+
s: 0,
|
|
1009
|
+
v: small.reduce((sum, node)=>sum + node.v, 0),
|
|
1010
|
+
vs: sumValues(small),
|
|
1011
|
+
w: 0,
|
|
1012
|
+
x: 0,
|
|
1013
|
+
y: 0
|
|
1014
|
+
};
|
|
1015
|
+
const grouped = nodes.find((node)=>node.g !== undefined);
|
|
1016
|
+
if (grouped) {
|
|
1017
|
+
bucket.g = label;
|
|
1018
|
+
bucket.l = grouped.l;
|
|
1019
|
+
}
|
|
1020
|
+
if (weighted) {
|
|
1021
|
+
Object.defineProperty(bucket, WEIGHT_KEY, {
|
|
1022
|
+
configurable: true,
|
|
1023
|
+
value: small.reduce((sum, node)=>sum + weightOf(node), 0),
|
|
1024
|
+
writable: true
|
|
1025
|
+
});
|
|
1026
|
+
}
|
|
1027
|
+
kept.push(bucket);
|
|
1028
|
+
return kept;
|
|
948
1029
|
}
|
|
1030
|
+
function sumValues(nodes) {
|
|
1031
|
+
const totals = {};
|
|
1032
|
+
for (const node of nodes){
|
|
1033
|
+
for (const [key, value] of Object.entries(node.vs || {})){
|
|
1034
|
+
totals[key] = (totals[key] || 0) + value;
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
return Object.keys(totals).length ? totals : undefined;
|
|
1038
|
+
}
|
|
1039
|
+
const VALUE_SCALES = {
|
|
1040
|
+
log: (value)=>Math.log1p(Math.max(value, 0)),
|
|
1041
|
+
sqrt: (value)=>Math.sqrt(Math.max(value, 0))
|
|
1042
|
+
};
|
|
949
1043
|
function flattenNodes(nodes) {
|
|
950
1044
|
const flat = nodes.slice();
|
|
951
1045
|
for (const node of nodes){
|
|
@@ -955,22 +1049,33 @@ function buildHierarchy(values, gidx, keys, layout, parentGroupValue) {
|
|
|
955
1049
|
}
|
|
956
1050
|
return flat;
|
|
957
1051
|
}
|
|
958
|
-
function layoutLevel(nodes, rect, layout, parentSum) {
|
|
1052
|
+
function layoutLevel(nodes, rect, layout, weighted, parentSum) {
|
|
959
1053
|
if (parentSum !== undefined) {
|
|
960
1054
|
for (const node of nodes){
|
|
961
1055
|
node.gs = parentSum;
|
|
962
1056
|
}
|
|
963
1057
|
}
|
|
964
|
-
packInto(nodes, rect);
|
|
1058
|
+
packInto(nodes, rect, weighted);
|
|
965
1059
|
for (const node of nodes){
|
|
966
1060
|
if (node._children?.length) {
|
|
967
|
-
layoutLevel(node._children, getSubRect(node, rect, layout), layout, node.s);
|
|
1061
|
+
layoutLevel(node._children, getSubRect(node, rect, layout), layout, weighted, node.s);
|
|
968
1062
|
}
|
|
969
1063
|
}
|
|
970
1064
|
}
|
|
971
1065
|
function layoutNodes(nodes, rect, layout) {
|
|
972
|
-
layoutLevel(nodes, rect, layout);
|
|
1066
|
+
layoutLevel(nodes, rect, layout, isWeighted(layout));
|
|
973
1067
|
applyHeaderBoxes(flattenNodes(nodes), layout);
|
|
1068
|
+
}
|
|
1069
|
+
function isWeighted(layout) {
|
|
1070
|
+
const scale = layout.valueScale;
|
|
1071
|
+
return scale !== 'linear' && scale !== undefined;
|
|
1072
|
+
}
|
|
1073
|
+
function scaleFor(layout) {
|
|
1074
|
+
const scale = layout.valueScale;
|
|
1075
|
+
if (typeof scale === 'function') {
|
|
1076
|
+
return scale;
|
|
1077
|
+
}
|
|
1078
|
+
return VALUE_SCALES[scale] || ((value)=>value);
|
|
974
1079
|
}
|
|
975
1080
|
function getSubRect(sq, rect, layout) {
|
|
976
1081
|
const { borderWidth, captions, displayMode } = layout;
|
|
@@ -998,12 +1103,21 @@ function buildHierarchy(values, gidx, keys, layout, parentGroupValue) {
|
|
|
998
1103
|
function buildNodes(tree, keys, layout) {
|
|
999
1104
|
const { groups, leafKey, unsorted } = layout;
|
|
1000
1105
|
const input = helpers.isObject(tree) ? normalizeTreeToArray(keys, leafKey, tree) : tree;
|
|
1106
|
+
const weighted = isWeighted(layout);
|
|
1107
|
+
let nodes;
|
|
1001
1108
|
if (groups.length) {
|
|
1002
|
-
|
|
1003
|
-
}
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1109
|
+
nodes = buildHierarchy(input, 0, keys, layout, weighted);
|
|
1110
|
+
} else {
|
|
1111
|
+
nodes = toNodes(input || [], keys);
|
|
1112
|
+
if (weighted) {
|
|
1113
|
+
applyValueScale(nodes, scaleFor(layout));
|
|
1114
|
+
}
|
|
1115
|
+
if (layout.others) {
|
|
1116
|
+
nodes = applyOthers(nodes, layout.others, weighted);
|
|
1117
|
+
}
|
|
1118
|
+
if (!unsorted) {
|
|
1119
|
+
sortNodes(nodes, weighted);
|
|
1120
|
+
}
|
|
1007
1121
|
}
|
|
1008
1122
|
return nodes;
|
|
1009
1123
|
}
|
|
@@ -1027,6 +1141,9 @@ function buildHierarchy(values, gidx, keys, layout, parentGroupValue) {
|
|
|
1027
1141
|
}
|
|
1028
1142
|
}
|
|
1029
1143
|
|
|
1144
|
+
function clampFraction(value, fallback) {
|
|
1145
|
+
return value === undefined ? fallback : Math.min(Math.max(value, 0), 1);
|
|
1146
|
+
}
|
|
1030
1147
|
function registerTooltipPositioner() {
|
|
1031
1148
|
const tooltipPlugin = chart_js.registry.plugins.get('tooltip');
|
|
1032
1149
|
if (!tooltipPlugin || tooltipPlugin.positioners.treemap) {
|
|
@@ -1061,12 +1178,9 @@ class TreemapController extends chart_js.DatasetController {
|
|
|
1061
1178
|
const w = xScale.right - xScale.left;
|
|
1062
1179
|
const h = yScale.bottom - yScale.top;
|
|
1063
1180
|
const rect = {
|
|
1064
|
-
h,
|
|
1181
|
+
...this._regionBounds(w, h),
|
|
1065
1182
|
rtl: !!this.options.rtl,
|
|
1066
|
-
unsorted: !!this.options.unsorted
|
|
1067
|
-
w,
|
|
1068
|
-
x: 0,
|
|
1069
|
-
y: 0
|
|
1183
|
+
unsorted: !!this.options.unsorted
|
|
1070
1184
|
};
|
|
1071
1185
|
if (rectNotEqual(this._rect, rect)) {
|
|
1072
1186
|
this._rect = rect;
|
|
@@ -1079,6 +1193,31 @@ class TreemapController extends chart_js.DatasetController {
|
|
|
1079
1193
|
yScale.configure();
|
|
1080
1194
|
}
|
|
1081
1195
|
}
|
|
1196
|
+
_regionBounds(w, h) {
|
|
1197
|
+
const region = this.options.region;
|
|
1198
|
+
if (!region) {
|
|
1199
|
+
return {
|
|
1200
|
+
h,
|
|
1201
|
+
w,
|
|
1202
|
+
x: 0,
|
|
1203
|
+
y: 0
|
|
1204
|
+
};
|
|
1205
|
+
}
|
|
1206
|
+
const left = clampFraction(region.left, 0);
|
|
1207
|
+
const top = clampFraction(region.top, 0);
|
|
1208
|
+
const width = clampFraction(region.width, 1);
|
|
1209
|
+
const height = clampFraction(region.height, 1);
|
|
1210
|
+
if (!this._regionWarned && (left + width > 1 || top + height > 1)) {
|
|
1211
|
+
this._regionWarned = true;
|
|
1212
|
+
console.warn('chartjs-chart-treemap: a dataset region reaches outside the chart area and is clipped to it.');
|
|
1213
|
+
}
|
|
1214
|
+
return {
|
|
1215
|
+
h: h * Math.min(height, 1 - top),
|
|
1216
|
+
w: w * Math.min(width, 1 - left),
|
|
1217
|
+
x: w * left,
|
|
1218
|
+
y: h * top
|
|
1219
|
+
};
|
|
1220
|
+
}
|
|
1082
1221
|
_layoutOptions() {
|
|
1083
1222
|
const dataset = this.getDataset();
|
|
1084
1223
|
const options = this.options;
|
|
@@ -1088,8 +1227,10 @@ class TreemapController extends chart_js.DatasetController {
|
|
|
1088
1227
|
displayMode: options.displayMode,
|
|
1089
1228
|
groups: options.groups || [],
|
|
1090
1229
|
leafKey: options.leafKey,
|
|
1230
|
+
others: options.others || false,
|
|
1091
1231
|
spacing: helpers.valueOrDefault(dataset.spacing, 0),
|
|
1092
|
-
unsorted: !!options.unsorted
|
|
1232
|
+
unsorted: !!options.unsorted,
|
|
1233
|
+
valueScale: options.valueScale || 'linear'
|
|
1093
1234
|
};
|
|
1094
1235
|
}
|
|
1095
1236
|
buildOrUpdateElements(_resetNewElements) {
|
|
@@ -1144,8 +1285,10 @@ class TreemapController extends chart_js.DatasetController {
|
|
|
1144
1285
|
}
|
|
1145
1286
|
const dataset = this.getDataset();
|
|
1146
1287
|
const label = node.g ?? node._data?.label ?? dataset.label;
|
|
1288
|
+
const text = label === undefined ? '' : `${label}`;
|
|
1289
|
+
const others = node.isOthers ? node._data?.others?.length : undefined;
|
|
1147
1290
|
return {
|
|
1148
|
-
label:
|
|
1291
|
+
label: others ? `${text} (${others} items)` : text,
|
|
1149
1292
|
value: `${node.v}`
|
|
1150
1293
|
};
|
|
1151
1294
|
}
|
|
@@ -1207,6 +1350,7 @@ class TreemapController extends chart_js.DatasetController {
|
|
|
1207
1350
|
super(chart, datasetIndex);
|
|
1208
1351
|
this._rect = undefined;
|
|
1209
1352
|
this._rectChanged = true;
|
|
1353
|
+
this._regionWarned = false;
|
|
1210
1354
|
this._nodes = [];
|
|
1211
1355
|
this._layoutDirty = true;
|
|
1212
1356
|
this._fingerprint = undefined;
|
|
@@ -1231,10 +1375,13 @@ TreemapController.defaults = {
|
|
|
1231
1375
|
groups: [],
|
|
1232
1376
|
key: '',
|
|
1233
1377
|
leafKey: '_leaf',
|
|
1378
|
+
others: false,
|
|
1379
|
+
region: undefined,
|
|
1234
1380
|
rtl: layoutDefaults.rtl,
|
|
1235
1381
|
spacing: layoutDefaults.spacing,
|
|
1236
1382
|
sumKeys: [],
|
|
1237
|
-
unsorted: false
|
|
1383
|
+
unsorted: false,
|
|
1384
|
+
valueScale: 'linear'
|
|
1238
1385
|
};
|
|
1239
1386
|
TreemapController.descriptors = {
|
|
1240
1387
|
_indexable: false,
|