chartjs-chart-treemap 1.0.0-beta.4 → 1.0.3
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/LICENSE +1 -1
- package/README.md +4 -65
- package/dist/chartjs-chart-treemap.esm.js +180 -53
- package/dist/chartjs-chart-treemap.js +181 -65
- package/dist/chartjs-chart-treemap.min.js +3 -3
- package/package.json +34 -19
- package/types/index.esm.d.ts +143 -0
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -1,74 +1,13 @@
|
|
|
1
1
|
# chartjs-chart-treemap
|
|
2
2
|
|
|
3
|
-
[Chart.js](https://www.chartjs.org/) **v3.
|
|
3
|
+
[Chart.js](https://www.chartjs.org/) **v3.6.0** module for creating treemap charts. Implementation for Chart.js v2 is in [2.x branch](https://github.com/kurkle/chartjs-chart-treemap/tree/2.x)
|
|
4
4
|
|
|
5
|
-
[](https://www.npmjs.com/package/chartjs-chart-
|
|
5
|
+
[](https://www.npmjs.com/package/chartjs-chart-treemap)
|
|
6
6
|
[](https://github.com/kurkle/chartjs-chart-treemap/releases/latest)
|
|
7
7
|

|
|
8
|
+
[](https://chartjs-chart-treemap.pages.dev)
|
|
8
9
|

|
|
9
|
-
|
|
10
|
-
## Documentation
|
|
11
|
-
|
|
12
|
-
To create a treemap chart, include chartjs-chart-treemap.js after chart.js and then create the chart by setting the `type` attribute to `'treemap'`
|
|
13
|
-
|
|
14
|
-
```js
|
|
15
|
-
new Chart(ctx, {
|
|
16
|
-
type: 'treemap',
|
|
17
|
-
tree: dataObject,
|
|
18
|
-
key: 'value',
|
|
19
|
-
groups: ['main', 'sub']
|
|
20
|
-
});
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
## Configuration
|
|
24
|
-
|
|
25
|
-
Tree data should be provided in `tree` property of dataset. `data` is then automatically build. `key` defines the key name in data objects to use for value. `groups` array can be provided to display multiple levels of hierarchy.
|
|
26
|
-
Data is summarized to groups internally.
|
|
27
|
-
|
|
28
|
-
```js
|
|
29
|
-
new Chart(ctx, {
|
|
30
|
-
type: 'treemap',
|
|
31
|
-
data: {
|
|
32
|
-
datasets: [{
|
|
33
|
-
label: 'Basic treemap',
|
|
34
|
-
tree: [6,6,5,4,3,2,2,1],
|
|
35
|
-
font: {
|
|
36
|
-
color: '#000',
|
|
37
|
-
family: 'serif',
|
|
38
|
-
size: 12,
|
|
39
|
-
style: 'normal',
|
|
40
|
-
},
|
|
41
|
-
backgroundColor: function(ctx) {
|
|
42
|
-
var value = ctx.dataset.data[ctx.dataIndex];
|
|
43
|
-
var alpha = (value + 3) / 10;
|
|
44
|
-
return Color('blue').alpha(alpha).rgbString();
|
|
45
|
-
},
|
|
46
|
-
rtl: false // control in which direction the squares are positioned
|
|
47
|
-
}]
|
|
48
|
-
},
|
|
49
|
-
});
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
### Note about chartjs-plugin-datalabels
|
|
53
|
-
|
|
54
|
-
Treemap is not using any scales currently and thats why [chartjs-plugin-datalabels](https://chartjs-plugin-datalabels.netlify.app/) plugin does not work with it.
|
|
55
|
-
When other charts are using datalables on the same page, you'll need to disable the plugin for treemap charts:
|
|
56
|
-
|
|
57
|
-
```js
|
|
58
|
-
new Chart(ctx, {
|
|
59
|
-
type: 'treemap',
|
|
60
|
-
data: (...),
|
|
61
|
-
options: {
|
|
62
|
-
plugins: {
|
|
63
|
-
datalabels: false
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
});
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
## Example
|
|
70
|
-
|
|
71
|
-
[Live examples @codepen.io](https://codepen.io/kurkle/full/oNjXJwe)
|
|
10
|
+
[](https://coveralls.io/github/kurkle/chartjs-chart-treemap)
|
|
72
11
|
|
|
73
12
|

|
|
74
13
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* chartjs-chart-treemap v1.0.
|
|
3
|
-
* https://
|
|
2
|
+
* chartjs-chart-treemap v1.0.3
|
|
3
|
+
* https://chartjs-chart-treemap.pages.dev/
|
|
4
4
|
* (c) 2021 Jukka Kurkela
|
|
5
5
|
* Released under the MIT license
|
|
6
6
|
*/
|
|
7
|
-
import { DatasetController, Element } from 'chart.js';
|
|
8
|
-
import { toFont, valueOrDefault } from 'chart.js/helpers';
|
|
7
|
+
import { Chart, registry, DatasetController, Element } from 'chart.js';
|
|
8
|
+
import { toFont, valueOrDefault, isArray } from 'chart.js/helpers';
|
|
9
9
|
|
|
10
10
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat
|
|
11
11
|
function flatten(input) {
|
|
@@ -108,7 +108,15 @@ function sum(values, key) {
|
|
|
108
108
|
return s;
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
+
function requireVersion(min, ver) {
|
|
112
|
+
const parts = ver.split('.');
|
|
113
|
+
if (!min.split('.').reduce((a, c, i) => a && c <= parts[i], true)) {
|
|
114
|
+
throw new Error(`Chart.js v${ver} is not supported. v${min} or newer is required.`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
111
118
|
function round(v, n) {
|
|
119
|
+
// @ts-ignore
|
|
112
120
|
return (+(Math.round(v + 'e+' + n) + 'e-' + n)) || 0;
|
|
113
121
|
}
|
|
114
122
|
|
|
@@ -326,7 +334,7 @@ function squarify(values, rectangle, key, grp, lvl, gsum) {
|
|
|
326
334
|
const gval = (idx) => grp && tmp[idx][grp];
|
|
327
335
|
|
|
328
336
|
for (i = 0; i < n; ++i) {
|
|
329
|
-
o = {value: val(i), groupSum: gsum, _data: values[tmp[i]._idx]};
|
|
337
|
+
o = {value: val(i), groupSum: gsum, _data: values[tmp[i]._idx], level: undefined, group: undefined};
|
|
330
338
|
if (grp) {
|
|
331
339
|
o.level = lvl;
|
|
332
340
|
o.group = gval(i);
|
|
@@ -345,6 +353,8 @@ function squarify(values, rectangle, key, grp, lvl, gsum) {
|
|
|
345
353
|
return flatten(rows);
|
|
346
354
|
}
|
|
347
355
|
|
|
356
|
+
var version = "1.0.3";
|
|
357
|
+
|
|
348
358
|
function rectNotEqual(r1, r2) {
|
|
349
359
|
return !r1 || !r2
|
|
350
360
|
|| r1.x !== r2.x
|
|
@@ -380,35 +390,47 @@ function shouldDrawCaption(rect, font) {
|
|
|
380
390
|
|
|
381
391
|
function drawCaption(ctx, rect, item, opts, levels) {
|
|
382
392
|
ctx.save();
|
|
383
|
-
ctx.fillStyle = opts.color;
|
|
384
|
-
ctx.font = opts.font.string;
|
|
385
393
|
ctx.beginPath();
|
|
386
394
|
ctx.rect(rect.x, rect.y, rect.width, rect.height);
|
|
387
395
|
ctx.clip();
|
|
388
396
|
if (!('l' in item) || item.l === levels) {
|
|
389
|
-
ctx.textAlign = 'center';
|
|
390
|
-
ctx.textBaseline = 'middle';
|
|
391
397
|
drawLabels(ctx, item, rect);
|
|
392
|
-
} else if (opts.
|
|
393
|
-
ctx
|
|
394
|
-
ctx.textBaseline = 'top';
|
|
395
|
-
const x = opts.rtl ? rect.x + rect.width - opts.borderWidth - 3 : rect.x + opts.borderWidth + 3;
|
|
396
|
-
ctx.fillText(item.g, x, rect.y + opts.borderWidth + 3);
|
|
398
|
+
} else if (opts.captions && opts.captions.display) {
|
|
399
|
+
drawCaptionLabel(ctx, item, rect);
|
|
397
400
|
}
|
|
398
401
|
ctx.restore();
|
|
399
402
|
}
|
|
400
403
|
|
|
404
|
+
function drawCaptionLabel(ctx, item, rect) {
|
|
405
|
+
const opts = rect.options;
|
|
406
|
+
const captionsOpts = opts.captions || {};
|
|
407
|
+
const borderWidth = opts.borderWidth || 0;
|
|
408
|
+
const spacing = valueOrDefault(opts.spacing, 0) + borderWidth;
|
|
409
|
+
const color = (rect.active ? captionsOpts.hoverColor : captionsOpts.color) || captionsOpts.color;
|
|
410
|
+
const padding = captionsOpts.padding;
|
|
411
|
+
const align = captionsOpts.align || (opts.rtl ? 'right' : 'left');
|
|
412
|
+
const optFont = (rect.active ? captionsOpts.hoverFont : captionsOpts.font) || captionsOpts.font;
|
|
413
|
+
const font = toFont(optFont);
|
|
414
|
+
const x = calculateX(rect, align, padding, borderWidth);
|
|
415
|
+
ctx.fillStyle = color;
|
|
416
|
+
ctx.font = font.string;
|
|
417
|
+
ctx.textAlign = align;
|
|
418
|
+
ctx.textBaseline = 'middle';
|
|
419
|
+
ctx.fillText(captionsOpts.formatter || item.g, x, rect.y + padding + spacing + (font.lineHeight / 2));
|
|
420
|
+
}
|
|
421
|
+
|
|
401
422
|
function drawDivider(ctx, rect) {
|
|
402
423
|
const opts = rect.options;
|
|
424
|
+
const dividersOpts = opts.dividers || {};
|
|
403
425
|
const w = rect.width || rect.w;
|
|
404
426
|
const h = rect.height || rect.h;
|
|
405
427
|
|
|
406
428
|
ctx.save();
|
|
407
|
-
ctx.strokeStyle =
|
|
408
|
-
ctx.lineCap =
|
|
409
|
-
ctx.setLineDash(
|
|
410
|
-
ctx.lineDashOffset =
|
|
411
|
-
ctx.lineWidth =
|
|
429
|
+
ctx.strokeStyle = dividersOpts.lineColor || 'black';
|
|
430
|
+
ctx.lineCap = dividersOpts.lineCapStyle;
|
|
431
|
+
ctx.setLineDash(dividersOpts.lineDash || []);
|
|
432
|
+
ctx.lineDashOffset = dividersOpts.lineDashOffset;
|
|
433
|
+
ctx.lineWidth = dividersOpts.lineWidth;
|
|
412
434
|
ctx.beginPath();
|
|
413
435
|
if (w > h) {
|
|
414
436
|
const w2 = w / 2;
|
|
@@ -423,12 +445,15 @@ function drawDivider(ctx, rect) {
|
|
|
423
445
|
ctx.restore();
|
|
424
446
|
}
|
|
425
447
|
|
|
426
|
-
function buildData(dataset, mainRect,
|
|
448
|
+
function buildData(dataset, mainRect, captions) {
|
|
427
449
|
const key = dataset.key || '';
|
|
428
450
|
let tree = dataset.tree || [];
|
|
429
451
|
const groups = dataset.groups || [];
|
|
430
452
|
const glen = groups.length;
|
|
431
|
-
const sp = (dataset.spacing
|
|
453
|
+
const sp = valueOrDefault(dataset.spacing, 0) + valueOrDefault(dataset.borderWidth, 0);
|
|
454
|
+
const captionsFont = captions.font || {};
|
|
455
|
+
const font = toFont(captionsFont);
|
|
456
|
+
const padding = valueOrDefault(captions.padding, 3);
|
|
432
457
|
|
|
433
458
|
function recur(gidx, rect, parent, gs) {
|
|
434
459
|
const g = groups[gidx];
|
|
@@ -440,10 +465,9 @@ function buildData(dataset, mainRect, font) {
|
|
|
440
465
|
if (gidx < glen - 1) {
|
|
441
466
|
gsq.forEach((sq) => {
|
|
442
467
|
subRect = {x: sq.x + sp, y: sq.y + sp, w: sq.w - 2 * sp, h: sq.h - 2 * sp};
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
subRect.
|
|
446
|
-
subRect.h -= font.lineHeight;
|
|
468
|
+
if (valueOrDefault(captions.display, true) && shouldDrawCaption(sq, font)) {
|
|
469
|
+
subRect.y += font.lineHeight + padding * 2;
|
|
470
|
+
subRect.h -= font.lineHeight + padding * 2;
|
|
447
471
|
}
|
|
448
472
|
ret.push(...recur(gidx + 1, subRect, sq.g, sq.s));
|
|
449
473
|
});
|
|
@@ -462,13 +486,60 @@ function buildData(dataset, mainRect, font) {
|
|
|
462
486
|
|
|
463
487
|
function drawLabels(ctx, item, rect) {
|
|
464
488
|
const opts = rect.options;
|
|
465
|
-
const
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
489
|
+
const labelsOpts = opts.labels;
|
|
490
|
+
if (!labelsOpts || !labelsOpts.display) {
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
const optColor = (rect.active ? labelsOpts.hoverColor : labelsOpts.color) || labelsOpts.color;
|
|
494
|
+
const optFont = (rect.active ? labelsOpts.hoverFont : labelsOpts.font) || labelsOpts.font;
|
|
495
|
+
const font = toFont(optFont);
|
|
496
|
+
const lh = font.lineHeight;
|
|
497
|
+
const label = labelsOpts.formatter;
|
|
498
|
+
if (label) {
|
|
499
|
+
const labels = isArray(label) ? label : [label];
|
|
500
|
+
const xyPoint = calculateXYLabel(opts, rect, labels, lh);
|
|
501
|
+
ctx.font = font.string;
|
|
502
|
+
ctx.textAlign = labelsOpts.align;
|
|
503
|
+
ctx.textBaseline = labelsOpts.position;
|
|
504
|
+
ctx.fillStyle = optColor;
|
|
505
|
+
labels.forEach((l, i) => ctx.fillText(l, xyPoint.x, xyPoint.y + i * lh));
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
function calculateXYLabel(options, rect, labels, lineHeight) {
|
|
510
|
+
const labelsOpts = options.labels;
|
|
511
|
+
const borderWidth = options.borderWidth || 0;
|
|
512
|
+
const {align, position, padding} = labelsOpts;
|
|
513
|
+
let x, y;
|
|
514
|
+
x = calculateX(rect, align, padding, borderWidth);
|
|
515
|
+
if (position === 'top') {
|
|
516
|
+
y = rect.y + padding + borderWidth;
|
|
517
|
+
} else if (position === 'bottom') {
|
|
518
|
+
y = rect.y + rect.height - padding - borderWidth - (labels.length - 1) * lineHeight;
|
|
519
|
+
} else {
|
|
520
|
+
y = rect.y + rect.height / 2 - labels.length * lineHeight / 4;
|
|
521
|
+
}
|
|
522
|
+
return {x, y};
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
function calculateX(rect, align, padding, borderWidth) {
|
|
526
|
+
if (align === 'left') {
|
|
527
|
+
return rect.x + padding + borderWidth;
|
|
528
|
+
} else if (align === 'right') {
|
|
529
|
+
return rect.x + rect.width - padding - borderWidth;
|
|
530
|
+
}
|
|
531
|
+
return rect.x + rect.width / 2;
|
|
469
532
|
}
|
|
470
533
|
|
|
471
534
|
class TreemapController extends DatasetController {
|
|
535
|
+
constructor(chart, datasetIndex) {
|
|
536
|
+
super(chart, datasetIndex);
|
|
537
|
+
|
|
538
|
+
this._rect = undefined;
|
|
539
|
+
this._key = undefined;
|
|
540
|
+
this._groups = undefined;
|
|
541
|
+
}
|
|
542
|
+
|
|
472
543
|
initialize() {
|
|
473
544
|
this.enableOptionSharing = true;
|
|
474
545
|
super.initialize();
|
|
@@ -479,7 +550,7 @@ class TreemapController extends DatasetController {
|
|
|
479
550
|
const meta = me.getMeta();
|
|
480
551
|
const dataset = me.getDataset();
|
|
481
552
|
const groups = dataset.groups || (dataset.groups = []);
|
|
482
|
-
const
|
|
553
|
+
const captions = dataset.captions ? dataset.captions : {};
|
|
483
554
|
const area = me.chart.chartArea;
|
|
484
555
|
const key = dataset.key || '';
|
|
485
556
|
const rtl = !!dataset.rtl;
|
|
@@ -490,8 +561,11 @@ class TreemapController extends DatasetController {
|
|
|
490
561
|
me._rect = mainRect;
|
|
491
562
|
me._groups = groups.slice();
|
|
492
563
|
me._key = key;
|
|
493
|
-
|
|
564
|
+
|
|
565
|
+
dataset.data = buildData(dataset, mainRect, captions);
|
|
566
|
+
// @ts-ignore using private stuff
|
|
494
567
|
me._dataCheck();
|
|
568
|
+
// @ts-ignore using private stuff
|
|
495
569
|
me._resyncElements();
|
|
496
570
|
}
|
|
497
571
|
|
|
@@ -501,7 +575,7 @@ class TreemapController extends DatasetController {
|
|
|
501
575
|
resolveDataElementOptions(index, mode) {
|
|
502
576
|
const options = super.resolveDataElementOptions(index, mode);
|
|
503
577
|
const result = Object.isFrozen(options) ? Object.assign({}, options) : options;
|
|
504
|
-
result.font = toFont(options.
|
|
578
|
+
result.font = toFont(options.captions.font);
|
|
505
579
|
return result;
|
|
506
580
|
}
|
|
507
581
|
|
|
@@ -540,13 +614,11 @@ class TreemapController extends DatasetController {
|
|
|
540
614
|
for (let i = 0, ilen = metadata.length; i < ilen; ++i) {
|
|
541
615
|
const rect = metadata[i];
|
|
542
616
|
const item = data[i];
|
|
543
|
-
|
|
617
|
+
const dividersOpts = rect.options.dividers || {};
|
|
618
|
+
if (dividersOpts.display && item._data.children.length > 1) {
|
|
544
619
|
drawDivider(ctx, rect);
|
|
545
620
|
}
|
|
546
621
|
}
|
|
547
|
-
if (this.getDataset().groupDividers) {
|
|
548
|
-
drawDivider(ctx, this._rect);
|
|
549
|
-
}
|
|
550
622
|
}
|
|
551
623
|
|
|
552
624
|
_drawRects(ctx, data, metadata, levels) {
|
|
@@ -556,7 +628,7 @@ class TreemapController extends DatasetController {
|
|
|
556
628
|
if (!rect.hidden) {
|
|
557
629
|
rect.draw(ctx);
|
|
558
630
|
const opts = rect.options;
|
|
559
|
-
if (shouldDrawCaption(rect, opts.font)
|
|
631
|
+
if (shouldDrawCaption(rect, opts.captions.font)) {
|
|
560
632
|
drawCaption(ctx, rect, item, opts, levels);
|
|
561
633
|
}
|
|
562
634
|
}
|
|
@@ -578,16 +650,25 @@ class TreemapController extends DatasetController {
|
|
|
578
650
|
|
|
579
651
|
TreemapController.id = 'treemap';
|
|
580
652
|
|
|
653
|
+
TreemapController.version = version;
|
|
654
|
+
|
|
581
655
|
TreemapController.defaults = {
|
|
582
656
|
dataElementType: 'treemap',
|
|
583
657
|
|
|
584
|
-
groupLabels: true,
|
|
585
658
|
borderWidth: 0,
|
|
586
659
|
spacing: 0.5,
|
|
587
|
-
|
|
588
|
-
|
|
660
|
+
dividers: {
|
|
661
|
+
display: false,
|
|
662
|
+
lineWidth: 1,
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
};
|
|
589
666
|
|
|
667
|
+
TreemapController.descriptors = {
|
|
668
|
+
_scriptable: true,
|
|
669
|
+
_indexable: false
|
|
590
670
|
};
|
|
671
|
+
|
|
591
672
|
TreemapController.overrides = {
|
|
592
673
|
interaction: {
|
|
593
674
|
mode: 'point',
|
|
@@ -629,6 +710,33 @@ TreemapController.overrides = {
|
|
|
629
710
|
},
|
|
630
711
|
};
|
|
631
712
|
|
|
713
|
+
TreemapController.beforeRegister = function() {
|
|
714
|
+
requireVersion('3.6', Chart.version);
|
|
715
|
+
};
|
|
716
|
+
|
|
717
|
+
TreemapController.afterRegister = function() {
|
|
718
|
+
const tooltipPlugin = registry.plugins.get('tooltip');
|
|
719
|
+
if (tooltipPlugin) {
|
|
720
|
+
tooltipPlugin.positioners.treemap = function(active) {
|
|
721
|
+
if (!active.length) {
|
|
722
|
+
return false;
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
const item = active[active.length - 1];
|
|
726
|
+
const el = item.element;
|
|
727
|
+
|
|
728
|
+
return el.tooltipPosition();
|
|
729
|
+
};
|
|
730
|
+
}
|
|
731
|
+
};
|
|
732
|
+
|
|
733
|
+
TreemapController.afterUnregister = function() {
|
|
734
|
+
const tooltipPlugin = registry.plugins.get('tooltip');
|
|
735
|
+
if (tooltipPlugin) {
|
|
736
|
+
delete tooltipPlugin.positioners.treemap;
|
|
737
|
+
}
|
|
738
|
+
};
|
|
739
|
+
|
|
632
740
|
/**
|
|
633
741
|
* Helper function to get the bounds of the rect
|
|
634
742
|
* @param {TreemapElement} rect the rect
|
|
@@ -730,7 +838,6 @@ class TreemapElement extends Element {
|
|
|
730
838
|
ctx.fillStyle = options.backgroundColor;
|
|
731
839
|
ctx.fillRect(inner.x, inner.y, inner.w, inner.h);
|
|
732
840
|
}
|
|
733
|
-
|
|
734
841
|
ctx.restore();
|
|
735
842
|
}
|
|
736
843
|
|
|
@@ -766,20 +873,40 @@ class TreemapElement extends Element {
|
|
|
766
873
|
TreemapElement.id = 'treemap';
|
|
767
874
|
|
|
768
875
|
TreemapElement.defaults = {
|
|
769
|
-
borderSkipped: undefined,
|
|
770
876
|
borderWidth: undefined,
|
|
771
|
-
color: undefined,
|
|
772
|
-
dividerCapStyle: 'butt',
|
|
773
|
-
dividerColor: 'black',
|
|
774
|
-
dividerDash: undefined,
|
|
775
|
-
dividerDashOffset: 0,
|
|
776
|
-
dividerWidth: 0,
|
|
777
|
-
font: {},
|
|
778
|
-
groupDividers: false,
|
|
779
|
-
groupLabels: undefined,
|
|
780
877
|
spacing: undefined,
|
|
781
878
|
label: undefined,
|
|
782
|
-
rtl: undefined
|
|
879
|
+
rtl: undefined,
|
|
880
|
+
dividers: {
|
|
881
|
+
display: false,
|
|
882
|
+
lineCapStyle: 'butt',
|
|
883
|
+
lineColor: 'black',
|
|
884
|
+
lineDash: undefined,
|
|
885
|
+
lineDashOffset: 0,
|
|
886
|
+
lineWidth: 0,
|
|
887
|
+
},
|
|
888
|
+
captions: {
|
|
889
|
+
align: undefined,
|
|
890
|
+
color: undefined,
|
|
891
|
+
display: true,
|
|
892
|
+
formatter: (ctx) => ctx.raw.g || '',
|
|
893
|
+
font: {},
|
|
894
|
+
padding: 3
|
|
895
|
+
},
|
|
896
|
+
labels: {
|
|
897
|
+
align: 'center',
|
|
898
|
+
color: undefined,
|
|
899
|
+
display: false,
|
|
900
|
+
formatter: (ctx) => ctx.raw.g ? [ctx.raw.g, ctx.raw.v] : ctx.raw.v,
|
|
901
|
+
font: {},
|
|
902
|
+
position: 'middle',
|
|
903
|
+
padding: 3
|
|
904
|
+
}
|
|
905
|
+
};
|
|
906
|
+
|
|
907
|
+
TreemapElement.descriptors = {
|
|
908
|
+
_scriptable: true,
|
|
909
|
+
_indexable: false
|
|
783
910
|
};
|
|
784
911
|
|
|
785
912
|
TreemapElement.defaultRoutes = {
|
|
@@ -787,4 +914,4 @@ TreemapElement.defaultRoutes = {
|
|
|
787
914
|
borderColor: 'borderColor'
|
|
788
915
|
};
|
|
789
916
|
|
|
790
|
-
export {
|
|
917
|
+
export { TreemapController, TreemapElement };
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* chartjs-chart-treemap v1.0.
|
|
3
|
-
* https://
|
|
2
|
+
* chartjs-chart-treemap v1.0.3
|
|
3
|
+
* https://chartjs-chart-treemap.pages.dev/
|
|
4
4
|
* (c) 2021 Jukka Kurkela
|
|
5
5
|
* Released under the MIT license
|
|
6
6
|
*/
|
|
7
7
|
(function (global, factory) {
|
|
8
8
|
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('chart.js'), require('chart.js/helpers')) :
|
|
9
9
|
typeof define === 'function' && define.amd ? define(['exports', 'chart.js', 'chart.js/helpers'], factory) :
|
|
10
|
-
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global[
|
|
11
|
-
}(this, (function (exports, chart_js, helpers) { 'use strict';
|
|
10
|
+
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["chartjs-chart-treemap"] = {}, global.Chart, global.Chart.helpers));
|
|
11
|
+
})(this, (function (exports, chart_js, helpers) { 'use strict';
|
|
12
12
|
|
|
13
13
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat
|
|
14
14
|
function flatten(input) {
|
|
@@ -111,7 +111,15 @@ function sum(values, key) {
|
|
|
111
111
|
return s;
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
+
function requireVersion(min, ver) {
|
|
115
|
+
const parts = ver.split('.');
|
|
116
|
+
if (!min.split('.').reduce((a, c, i) => a && c <= parts[i], true)) {
|
|
117
|
+
throw new Error(`Chart.js v${ver} is not supported. v${min} or newer is required.`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
114
121
|
function round(v, n) {
|
|
122
|
+
// @ts-ignore
|
|
115
123
|
return (+(Math.round(v + 'e+' + n) + 'e-' + n)) || 0;
|
|
116
124
|
}
|
|
117
125
|
|
|
@@ -329,7 +337,7 @@ function squarify(values, rectangle, key, grp, lvl, gsum) {
|
|
|
329
337
|
const gval = (idx) => grp && tmp[idx][grp];
|
|
330
338
|
|
|
331
339
|
for (i = 0; i < n; ++i) {
|
|
332
|
-
o = {value: val(i), groupSum: gsum, _data: values[tmp[i]._idx]};
|
|
340
|
+
o = {value: val(i), groupSum: gsum, _data: values[tmp[i]._idx], level: undefined, group: undefined};
|
|
333
341
|
if (grp) {
|
|
334
342
|
o.level = lvl;
|
|
335
343
|
o.group = gval(i);
|
|
@@ -348,6 +356,8 @@ function squarify(values, rectangle, key, grp, lvl, gsum) {
|
|
|
348
356
|
return flatten(rows);
|
|
349
357
|
}
|
|
350
358
|
|
|
359
|
+
var version = "1.0.3";
|
|
360
|
+
|
|
351
361
|
function rectNotEqual(r1, r2) {
|
|
352
362
|
return !r1 || !r2
|
|
353
363
|
|| r1.x !== r2.x
|
|
@@ -383,35 +393,47 @@ function shouldDrawCaption(rect, font) {
|
|
|
383
393
|
|
|
384
394
|
function drawCaption(ctx, rect, item, opts, levels) {
|
|
385
395
|
ctx.save();
|
|
386
|
-
ctx.fillStyle = opts.color;
|
|
387
|
-
ctx.font = opts.font.string;
|
|
388
396
|
ctx.beginPath();
|
|
389
397
|
ctx.rect(rect.x, rect.y, rect.width, rect.height);
|
|
390
398
|
ctx.clip();
|
|
391
399
|
if (!('l' in item) || item.l === levels) {
|
|
392
|
-
ctx.textAlign = 'center';
|
|
393
|
-
ctx.textBaseline = 'middle';
|
|
394
400
|
drawLabels(ctx, item, rect);
|
|
395
|
-
} else if (opts.
|
|
396
|
-
ctx
|
|
397
|
-
ctx.textBaseline = 'top';
|
|
398
|
-
const x = opts.rtl ? rect.x + rect.width - opts.borderWidth - 3 : rect.x + opts.borderWidth + 3;
|
|
399
|
-
ctx.fillText(item.g, x, rect.y + opts.borderWidth + 3);
|
|
401
|
+
} else if (opts.captions && opts.captions.display) {
|
|
402
|
+
drawCaptionLabel(ctx, item, rect);
|
|
400
403
|
}
|
|
401
404
|
ctx.restore();
|
|
402
405
|
}
|
|
403
406
|
|
|
407
|
+
function drawCaptionLabel(ctx, item, rect) {
|
|
408
|
+
const opts = rect.options;
|
|
409
|
+
const captionsOpts = opts.captions || {};
|
|
410
|
+
const borderWidth = opts.borderWidth || 0;
|
|
411
|
+
const spacing = helpers.valueOrDefault(opts.spacing, 0) + borderWidth;
|
|
412
|
+
const color = (rect.active ? captionsOpts.hoverColor : captionsOpts.color) || captionsOpts.color;
|
|
413
|
+
const padding = captionsOpts.padding;
|
|
414
|
+
const align = captionsOpts.align || (opts.rtl ? 'right' : 'left');
|
|
415
|
+
const optFont = (rect.active ? captionsOpts.hoverFont : captionsOpts.font) || captionsOpts.font;
|
|
416
|
+
const font = helpers.toFont(optFont);
|
|
417
|
+
const x = calculateX(rect, align, padding, borderWidth);
|
|
418
|
+
ctx.fillStyle = color;
|
|
419
|
+
ctx.font = font.string;
|
|
420
|
+
ctx.textAlign = align;
|
|
421
|
+
ctx.textBaseline = 'middle';
|
|
422
|
+
ctx.fillText(captionsOpts.formatter || item.g, x, rect.y + padding + spacing + (font.lineHeight / 2));
|
|
423
|
+
}
|
|
424
|
+
|
|
404
425
|
function drawDivider(ctx, rect) {
|
|
405
426
|
const opts = rect.options;
|
|
427
|
+
const dividersOpts = opts.dividers || {};
|
|
406
428
|
const w = rect.width || rect.w;
|
|
407
429
|
const h = rect.height || rect.h;
|
|
408
430
|
|
|
409
431
|
ctx.save();
|
|
410
|
-
ctx.strokeStyle =
|
|
411
|
-
ctx.lineCap =
|
|
412
|
-
ctx.setLineDash(
|
|
413
|
-
ctx.lineDashOffset =
|
|
414
|
-
ctx.lineWidth =
|
|
432
|
+
ctx.strokeStyle = dividersOpts.lineColor || 'black';
|
|
433
|
+
ctx.lineCap = dividersOpts.lineCapStyle;
|
|
434
|
+
ctx.setLineDash(dividersOpts.lineDash || []);
|
|
435
|
+
ctx.lineDashOffset = dividersOpts.lineDashOffset;
|
|
436
|
+
ctx.lineWidth = dividersOpts.lineWidth;
|
|
415
437
|
ctx.beginPath();
|
|
416
438
|
if (w > h) {
|
|
417
439
|
const w2 = w / 2;
|
|
@@ -426,12 +448,15 @@ function drawDivider(ctx, rect) {
|
|
|
426
448
|
ctx.restore();
|
|
427
449
|
}
|
|
428
450
|
|
|
429
|
-
function buildData(dataset, mainRect,
|
|
451
|
+
function buildData(dataset, mainRect, captions) {
|
|
430
452
|
const key = dataset.key || '';
|
|
431
453
|
let tree = dataset.tree || [];
|
|
432
454
|
const groups = dataset.groups || [];
|
|
433
455
|
const glen = groups.length;
|
|
434
|
-
const sp = (dataset.spacing
|
|
456
|
+
const sp = helpers.valueOrDefault(dataset.spacing, 0) + helpers.valueOrDefault(dataset.borderWidth, 0);
|
|
457
|
+
const captionsFont = captions.font || {};
|
|
458
|
+
const font = helpers.toFont(captionsFont);
|
|
459
|
+
const padding = helpers.valueOrDefault(captions.padding, 3);
|
|
435
460
|
|
|
436
461
|
function recur(gidx, rect, parent, gs) {
|
|
437
462
|
const g = groups[gidx];
|
|
@@ -443,10 +468,9 @@ function buildData(dataset, mainRect, font) {
|
|
|
443
468
|
if (gidx < glen - 1) {
|
|
444
469
|
gsq.forEach((sq) => {
|
|
445
470
|
subRect = {x: sq.x + sp, y: sq.y + sp, w: sq.w - 2 * sp, h: sq.h - 2 * sp};
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
subRect.
|
|
449
|
-
subRect.h -= font.lineHeight;
|
|
471
|
+
if (helpers.valueOrDefault(captions.display, true) && shouldDrawCaption(sq, font)) {
|
|
472
|
+
subRect.y += font.lineHeight + padding * 2;
|
|
473
|
+
subRect.h -= font.lineHeight + padding * 2;
|
|
450
474
|
}
|
|
451
475
|
ret.push(...recur(gidx + 1, subRect, sq.g, sq.s));
|
|
452
476
|
});
|
|
@@ -465,13 +489,60 @@ function buildData(dataset, mainRect, font) {
|
|
|
465
489
|
|
|
466
490
|
function drawLabels(ctx, item, rect) {
|
|
467
491
|
const opts = rect.options;
|
|
468
|
-
const
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
492
|
+
const labelsOpts = opts.labels;
|
|
493
|
+
if (!labelsOpts || !labelsOpts.display) {
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
const optColor = (rect.active ? labelsOpts.hoverColor : labelsOpts.color) || labelsOpts.color;
|
|
497
|
+
const optFont = (rect.active ? labelsOpts.hoverFont : labelsOpts.font) || labelsOpts.font;
|
|
498
|
+
const font = helpers.toFont(optFont);
|
|
499
|
+
const lh = font.lineHeight;
|
|
500
|
+
const label = labelsOpts.formatter;
|
|
501
|
+
if (label) {
|
|
502
|
+
const labels = helpers.isArray(label) ? label : [label];
|
|
503
|
+
const xyPoint = calculateXYLabel(opts, rect, labels, lh);
|
|
504
|
+
ctx.font = font.string;
|
|
505
|
+
ctx.textAlign = labelsOpts.align;
|
|
506
|
+
ctx.textBaseline = labelsOpts.position;
|
|
507
|
+
ctx.fillStyle = optColor;
|
|
508
|
+
labels.forEach((l, i) => ctx.fillText(l, xyPoint.x, xyPoint.y + i * lh));
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
function calculateXYLabel(options, rect, labels, lineHeight) {
|
|
513
|
+
const labelsOpts = options.labels;
|
|
514
|
+
const borderWidth = options.borderWidth || 0;
|
|
515
|
+
const {align, position, padding} = labelsOpts;
|
|
516
|
+
let x, y;
|
|
517
|
+
x = calculateX(rect, align, padding, borderWidth);
|
|
518
|
+
if (position === 'top') {
|
|
519
|
+
y = rect.y + padding + borderWidth;
|
|
520
|
+
} else if (position === 'bottom') {
|
|
521
|
+
y = rect.y + rect.height - padding - borderWidth - (labels.length - 1) * lineHeight;
|
|
522
|
+
} else {
|
|
523
|
+
y = rect.y + rect.height / 2 - labels.length * lineHeight / 4;
|
|
524
|
+
}
|
|
525
|
+
return {x, y};
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
function calculateX(rect, align, padding, borderWidth) {
|
|
529
|
+
if (align === 'left') {
|
|
530
|
+
return rect.x + padding + borderWidth;
|
|
531
|
+
} else if (align === 'right') {
|
|
532
|
+
return rect.x + rect.width - padding - borderWidth;
|
|
533
|
+
}
|
|
534
|
+
return rect.x + rect.width / 2;
|
|
472
535
|
}
|
|
473
536
|
|
|
474
537
|
class TreemapController extends chart_js.DatasetController {
|
|
538
|
+
constructor(chart, datasetIndex) {
|
|
539
|
+
super(chart, datasetIndex);
|
|
540
|
+
|
|
541
|
+
this._rect = undefined;
|
|
542
|
+
this._key = undefined;
|
|
543
|
+
this._groups = undefined;
|
|
544
|
+
}
|
|
545
|
+
|
|
475
546
|
initialize() {
|
|
476
547
|
this.enableOptionSharing = true;
|
|
477
548
|
super.initialize();
|
|
@@ -482,7 +553,7 @@ class TreemapController extends chart_js.DatasetController {
|
|
|
482
553
|
const meta = me.getMeta();
|
|
483
554
|
const dataset = me.getDataset();
|
|
484
555
|
const groups = dataset.groups || (dataset.groups = []);
|
|
485
|
-
const
|
|
556
|
+
const captions = dataset.captions ? dataset.captions : {};
|
|
486
557
|
const area = me.chart.chartArea;
|
|
487
558
|
const key = dataset.key || '';
|
|
488
559
|
const rtl = !!dataset.rtl;
|
|
@@ -493,8 +564,11 @@ class TreemapController extends chart_js.DatasetController {
|
|
|
493
564
|
me._rect = mainRect;
|
|
494
565
|
me._groups = groups.slice();
|
|
495
566
|
me._key = key;
|
|
496
|
-
|
|
567
|
+
|
|
568
|
+
dataset.data = buildData(dataset, mainRect, captions);
|
|
569
|
+
// @ts-ignore using private stuff
|
|
497
570
|
me._dataCheck();
|
|
571
|
+
// @ts-ignore using private stuff
|
|
498
572
|
me._resyncElements();
|
|
499
573
|
}
|
|
500
574
|
|
|
@@ -504,7 +578,7 @@ class TreemapController extends chart_js.DatasetController {
|
|
|
504
578
|
resolveDataElementOptions(index, mode) {
|
|
505
579
|
const options = super.resolveDataElementOptions(index, mode);
|
|
506
580
|
const result = Object.isFrozen(options) ? Object.assign({}, options) : options;
|
|
507
|
-
result.font = helpers.toFont(options.
|
|
581
|
+
result.font = helpers.toFont(options.captions.font);
|
|
508
582
|
return result;
|
|
509
583
|
}
|
|
510
584
|
|
|
@@ -543,13 +617,11 @@ class TreemapController extends chart_js.DatasetController {
|
|
|
543
617
|
for (let i = 0, ilen = metadata.length; i < ilen; ++i) {
|
|
544
618
|
const rect = metadata[i];
|
|
545
619
|
const item = data[i];
|
|
546
|
-
|
|
620
|
+
const dividersOpts = rect.options.dividers || {};
|
|
621
|
+
if (dividersOpts.display && item._data.children.length > 1) {
|
|
547
622
|
drawDivider(ctx, rect);
|
|
548
623
|
}
|
|
549
624
|
}
|
|
550
|
-
if (this.getDataset().groupDividers) {
|
|
551
|
-
drawDivider(ctx, this._rect);
|
|
552
|
-
}
|
|
553
625
|
}
|
|
554
626
|
|
|
555
627
|
_drawRects(ctx, data, metadata, levels) {
|
|
@@ -559,7 +631,7 @@ class TreemapController extends chart_js.DatasetController {
|
|
|
559
631
|
if (!rect.hidden) {
|
|
560
632
|
rect.draw(ctx);
|
|
561
633
|
const opts = rect.options;
|
|
562
|
-
if (shouldDrawCaption(rect, opts.font)
|
|
634
|
+
if (shouldDrawCaption(rect, opts.captions.font)) {
|
|
563
635
|
drawCaption(ctx, rect, item, opts, levels);
|
|
564
636
|
}
|
|
565
637
|
}
|
|
@@ -581,16 +653,25 @@ class TreemapController extends chart_js.DatasetController {
|
|
|
581
653
|
|
|
582
654
|
TreemapController.id = 'treemap';
|
|
583
655
|
|
|
656
|
+
TreemapController.version = version;
|
|
657
|
+
|
|
584
658
|
TreemapController.defaults = {
|
|
585
659
|
dataElementType: 'treemap',
|
|
586
660
|
|
|
587
|
-
groupLabels: true,
|
|
588
661
|
borderWidth: 0,
|
|
589
662
|
spacing: 0.5,
|
|
590
|
-
|
|
591
|
-
|
|
663
|
+
dividers: {
|
|
664
|
+
display: false,
|
|
665
|
+
lineWidth: 1,
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
};
|
|
592
669
|
|
|
670
|
+
TreemapController.descriptors = {
|
|
671
|
+
_scriptable: true,
|
|
672
|
+
_indexable: false
|
|
593
673
|
};
|
|
674
|
+
|
|
594
675
|
TreemapController.overrides = {
|
|
595
676
|
interaction: {
|
|
596
677
|
mode: 'point',
|
|
@@ -632,6 +713,33 @@ TreemapController.overrides = {
|
|
|
632
713
|
},
|
|
633
714
|
};
|
|
634
715
|
|
|
716
|
+
TreemapController.beforeRegister = function() {
|
|
717
|
+
requireVersion('3.6', chart_js.Chart.version);
|
|
718
|
+
};
|
|
719
|
+
|
|
720
|
+
TreemapController.afterRegister = function() {
|
|
721
|
+
const tooltipPlugin = chart_js.registry.plugins.get('tooltip');
|
|
722
|
+
if (tooltipPlugin) {
|
|
723
|
+
tooltipPlugin.positioners.treemap = function(active) {
|
|
724
|
+
if (!active.length) {
|
|
725
|
+
return false;
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
const item = active[active.length - 1];
|
|
729
|
+
const el = item.element;
|
|
730
|
+
|
|
731
|
+
return el.tooltipPosition();
|
|
732
|
+
};
|
|
733
|
+
}
|
|
734
|
+
};
|
|
735
|
+
|
|
736
|
+
TreemapController.afterUnregister = function() {
|
|
737
|
+
const tooltipPlugin = chart_js.registry.plugins.get('tooltip');
|
|
738
|
+
if (tooltipPlugin) {
|
|
739
|
+
delete tooltipPlugin.positioners.treemap;
|
|
740
|
+
}
|
|
741
|
+
};
|
|
742
|
+
|
|
635
743
|
/**
|
|
636
744
|
* Helper function to get the bounds of the rect
|
|
637
745
|
* @param {TreemapElement} rect the rect
|
|
@@ -733,7 +841,6 @@ class TreemapElement extends chart_js.Element {
|
|
|
733
841
|
ctx.fillStyle = options.backgroundColor;
|
|
734
842
|
ctx.fillRect(inner.x, inner.y, inner.w, inner.h);
|
|
735
843
|
}
|
|
736
|
-
|
|
737
844
|
ctx.restore();
|
|
738
845
|
}
|
|
739
846
|
|
|
@@ -769,20 +876,40 @@ class TreemapElement extends chart_js.Element {
|
|
|
769
876
|
TreemapElement.id = 'treemap';
|
|
770
877
|
|
|
771
878
|
TreemapElement.defaults = {
|
|
772
|
-
borderSkipped: undefined,
|
|
773
879
|
borderWidth: undefined,
|
|
774
|
-
color: undefined,
|
|
775
|
-
dividerCapStyle: 'butt',
|
|
776
|
-
dividerColor: 'black',
|
|
777
|
-
dividerDash: undefined,
|
|
778
|
-
dividerDashOffset: 0,
|
|
779
|
-
dividerWidth: 0,
|
|
780
|
-
font: {},
|
|
781
|
-
groupDividers: false,
|
|
782
|
-
groupLabels: undefined,
|
|
783
880
|
spacing: undefined,
|
|
784
881
|
label: undefined,
|
|
785
|
-
rtl: undefined
|
|
882
|
+
rtl: undefined,
|
|
883
|
+
dividers: {
|
|
884
|
+
display: false,
|
|
885
|
+
lineCapStyle: 'butt',
|
|
886
|
+
lineColor: 'black',
|
|
887
|
+
lineDash: undefined,
|
|
888
|
+
lineDashOffset: 0,
|
|
889
|
+
lineWidth: 0,
|
|
890
|
+
},
|
|
891
|
+
captions: {
|
|
892
|
+
align: undefined,
|
|
893
|
+
color: undefined,
|
|
894
|
+
display: true,
|
|
895
|
+
formatter: (ctx) => ctx.raw.g || '',
|
|
896
|
+
font: {},
|
|
897
|
+
padding: 3
|
|
898
|
+
},
|
|
899
|
+
labels: {
|
|
900
|
+
align: 'center',
|
|
901
|
+
color: undefined,
|
|
902
|
+
display: false,
|
|
903
|
+
formatter: (ctx) => ctx.raw.g ? [ctx.raw.g, ctx.raw.v] : ctx.raw.v,
|
|
904
|
+
font: {},
|
|
905
|
+
position: 'middle',
|
|
906
|
+
padding: 3
|
|
907
|
+
}
|
|
908
|
+
};
|
|
909
|
+
|
|
910
|
+
TreemapElement.descriptors = {
|
|
911
|
+
_scriptable: true,
|
|
912
|
+
_indexable: false
|
|
786
913
|
};
|
|
787
914
|
|
|
788
915
|
TreemapElement.defaultRoutes = {
|
|
@@ -792,25 +919,14 @@ TreemapElement.defaultRoutes = {
|
|
|
792
919
|
|
|
793
920
|
chart_js.Chart.register(TreemapController, TreemapElement);
|
|
794
921
|
|
|
795
|
-
const tooltipPlugin = chart_js.Chart.registry.plugins.get('tooltip');
|
|
796
|
-
tooltipPlugin.positioners.treemap = function(active) {
|
|
797
|
-
if (!active.length) {
|
|
798
|
-
return false;
|
|
799
|
-
}
|
|
800
|
-
|
|
801
|
-
const item = active[active.length - 1];
|
|
802
|
-
const el = item.element;
|
|
803
|
-
|
|
804
|
-
return el.tooltipPosition();
|
|
805
|
-
};
|
|
806
|
-
|
|
807
922
|
exports.flatten = flatten;
|
|
808
923
|
exports.group = group;
|
|
809
924
|
exports.index = index;
|
|
810
925
|
exports.isObject = isObject;
|
|
926
|
+
exports.requireVersion = requireVersion;
|
|
811
927
|
exports.sort = sort;
|
|
812
928
|
exports.sum = sum;
|
|
813
929
|
|
|
814
930
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
815
931
|
|
|
816
|
-
}))
|
|
932
|
+
}));
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* chartjs-chart-treemap v1.0.
|
|
3
|
-
* https://
|
|
2
|
+
* chartjs-chart-treemap v1.0.3
|
|
3
|
+
* https://chartjs-chart-treemap.pages.dev/
|
|
4
4
|
* (c) 2021 Jukka Kurkela
|
|
5
5
|
* Released under the MIT license
|
|
6
6
|
*/
|
|
7
|
-
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("chart.js"),require("chart.js/helpers")):"function"==typeof define&&define.amd?define(["exports","chart.js","chart.js/helpers"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self)["chartjs-chart-treemap"]={},t.Chart,t.Chart.helpers)}(this,(function(t,e,n){"use strict";function i(t){const e=[...t],n=[];for(;e.length;){const t=e.pop();Array.isArray(t)?e.push(...t):n.push(t)}return n.reverse()}function r(t,e,n,i,r){const o=Object.create(null),s=Object.create(null),a=[];let h,l,u,d;for(l=0,u=t.length;l<u;++l)d=t[l],i&&d[i]!==r||(h=d[e]||"",h in o||(o[h]=0,s[h]=[]),o[h]+=+d[n],s[h].push(d));return Object.keys(o).forEach((t=>{d={children:s[t]},d[n]=+o[t],d[e]=t,i&&(d[i]=r),a.push(d)})),a}function o(t){const e=typeof t;return"function"===e||"object"===e&&!!t}function s(t,e){let n,i=t.length;if(!i)return e;const r=o(t[0]);for(e=r?e:"v",n=0,i=t.length;n<i;++n)r?t[n]._idx=n:t[n]={v:t[n],_idx:n};return e}function a(t,e){e?t.sort(((t,n)=>+n[e]-+t[e])):t.sort(((t,e)=>+e-+t))}function h(t,e){let n,i,r;for(n=0,i=0,r=t.length;i<r;++i)n+=e?+t[i][e]:+t[i];return n}function l(t,e){return+(Math.round(t+"e+"+e)+"e-"+e)||0}function u(t,e,n,i){const r=t._normalized,o=e*r/n,s=Math.sqrt(r*o),a=r/s;return{d1:s,d2:a,w:"_ix"===i?s:a,h:"_ix"===i?a:s}}const d=(t,e)=>l(t.rtl?t.x+t.w-t._ix-e:t.x+t._ix,4);function c(t,e,n,i){const r={x:d(t,n.w),y:l(t.y+t._iy,4),w:l(n.w,4),h:l(n.h,4),a:l(e._normalized,4),v:e.value,s:i,_data:e._data};return e.group&&(r.g=e.group,r.l=e.level,r.gs=e.groupSum),r}class g{constructor(t){const e=this;t=t||{w:1,h:1},e.rtl=!!t.rtl,e.x=t.x||t.left||0,e.y=t.y||t.top||0,e._ix=0,e._iy=0,e.w=t.w||t.width||t.right-t.left,e.h=t.h||t.height||t.bottom-t.top}get area(){return this.w*this.h}get iw(){return this.w-this._ix}get ih(){return this.h-this._iy}get dir(){const t=this.ih;return t<=this.iw&&t>0?"y":"x"}get side(){return"x"===this.dir?this.iw:this.ih}map(t){const e=this,n=[],i=t.nsum,r=t.get(),o=e.dir,s=e.side,a=s*s,h="x"===o?"_ix":"_iy",l=i*i;let d=0,g=0;for(const i of r){const r=u(i,a,l,h);g+=r.d1,d=Math.max(d,r.d2),n.push(c(e,i,r,t.sum)),e[h]+=r.d1}return e["y"===o?"_ix":"_iy"]+=d,e[h]-=g,n}}const p=Math.min,f=Math.max;function m(t,e){const n=+e[t.key],i=n*t.ratio;return e._normalized=i,{min:p(t.min,n),max:f(t.max,n),sum:t.sum+n,nmin:p(t.nmin,i),nmax:f(t.nmax,i),nsum:t.nsum+i}}function x(t,e,n){t._arr.push(e),function(t,e){Object.assign(t,e)}(t,n)}class y{constructor(t,e){const n=this;n.key=t,n.ratio=e,n.reset()}get length(){return this._arr.length}reset(){const t=this;t._arr=[],t._hist=[],t.sum=0,t.nsum=0,t.min=1/0,t.max=-1/0,t.nmin=1/0,t.nmax=-1/0}push(t){x(this,t,m(this,t))}pushIf(t,e,...n){const i=m(this,t);if(!e((r=this,{min:r.min,max:r.max,sum:r.sum,nmin:r.nmin,nmax:r.nmax,nsum:r.nsum}),i,n))return t;var r;x(this,t,i)}get(){return this._arr}}function v(t,e,n){if(0===t.sum)return!0;const[i]=n,r=t.nsum*t.nsum,o=e.nsum*e.nsum,s=i*i,a=Math.max(s*t.nmax/r,r/(s*t.nmin));return Math.max(s*e.nmax/o,o/(s*e.nmin))<=a}function b(t,e,n,r,o,l){t=t||[];const u=[],d=new g(e),c=new y("value",d.area/h(t,n));let p=d.side;const f=t.length;let m,x;if(!f)return u;const b=t.slice();n=s(b,n),a(b,n);const w=t=>r&&b[t][r];for(m=0;m<f;++m)x={value:(_=m,n?+b[_][n]:+b[_]),groupSum:l,_data:t[b[m]._idx]},r&&(x.level=o,x.group=w(m)),x=c.pushIf(x,v,p),x&&(u.push(d.map(c)),p=d.side,c.reset(),c.push(x));var _;return c.length&&u.push(d.map(c)),i(u)}function w(t,e){if(!e)return!1;const n=t.width||t.w,i=t.height||t.h,r=2*e.lineHeight;return n>r&&i>r}function _(t,e,n,i,r){if(t.save(),t.fillStyle=i.color,t.font=i.font.string,t.beginPath(),t.rect(e.x,e.y,e.width,e.height),t.clip(),"l"in n&&n.l!==r){if(i.groupLabels){t.textAlign=i.rtl?"end":"start",t.textBaseline="top";const r=i.rtl?e.x+e.width-i.borderWidth-3:e.x+i.borderWidth+3;t.fillText(n.g,r,e.y+i.borderWidth+3)}}else t.textAlign="center",t.textBaseline="middle",function(t,e,n){const i=n.options,r=i.font.lineHeight,o=(i.label||e.g+"\n"+e.v).split("\n"),s=n.y+n.height/2-o.length*r/4;o.forEach(((e,i)=>t.fillText(e,n.x+n.width/2,s+i*r)))}(t,n,e);t.restore()}function D(t,e){const n=e.options,i=e.width||e.w,r=e.height||e.h;if(t.save(),t.strokeStyle=n.dividerColor||"black",t.lineCap=n.dividerCapStyle,t.setLineDash(n.dividerDash||[]),t.lineDashOffset=n.dividerDashOffset,t.lineWidth=n.dividerWidth,t.beginPath(),i>r){const n=i/2;t.moveTo(e.x+n,e.y),t.lineTo(e.x+n,e.y+r)}else{const n=r/2;t.moveTo(e.x,e.y+n),t.lineTo(e.x+i,e.y+n)}t.stroke(),t.restore()}class O extends e.DatasetController{initialize(){this.enableOptionSharing=!0,super.initialize()}update(t){const e=this,i=e.getMeta(),o=e.getDataset(),s=o.groups||(o.groups=[]),a=n.toFont(o.font,e.chart.options.font),h=e.chart.chartArea,l=o.key||"",u=!!o.rtl,d={x:h.left,y:h.top,w:h.right-h.left,h:h.bottom-h.top,rtl:u};var c,g;"reset"!==t&&(c=e._rect,g=d,c&&g&&c.x===g.x&&c.y===g.y&&c.w===g.w&&c.h===g.h)&&e._key===l&&!function(t,e){let n,i;if(t.lenght!==e.length)return!0;for(n=0,i=t.length;n<i;++n)if(t[n]!==e[n])return!0;return!1}(e._groups,s)||(e._rect=d,e._groups=s.slice(),e._key=l,o.data=function(t,e,i){const o=t.key||"";let s=t.tree||[];const a=t.groups||[],h=a.length,l=(t.spacing||0)+(t.borderWidth||0);return!s.length&&t.data.length&&(s=t.tree=t.data),h?function e(u,d,c,g){const p=a[u],f=u>0&&a[u-1],m=b(r(s,p,o,f,c),d,o,p,u,g),x=m.slice();let y;return u<h-1&&m.forEach((r=>{y={x:r.x+l,y:r.y+l,w:r.w-2*l,h:r.h-2*l},n.valueOrDefault(t.groupLabels,!0)&&w(r,i)&&(y.y+=i.lineHeight,y.h-=i.lineHeight),x.push(...e(u+1,y,r.g,r.s))})),x}(0,e):b(s,e,o)}(o,d,a),e._dataCheck(),e._resyncElements()),e.updateElements(i.data,0,i.data.length,t)}resolveDataElementOptions(t,e){const i=super.resolveDataElementOptions(t,e),r=Object.isFrozen(i)?Object.assign({},i):i;return r.font=n.toFont(i.font,this.chart.options.font),r}updateElements(t,e,n,i){const r=this,o="reset"===i,s=r.getDataset(),a=r._rect.options=r.resolveDataElementOptions(e,i),h=r.getSharedOptions(a),l=r.includeOptions(i,h);for(let a=e;a<e+n;a++){const e=s.data[a],n=h||r.resolveDataElementOptions(a,i),u=o?0:e.h-2*n.spacing,d=o?0:e.w-2*n.spacing,c={x:e.x+n.spacing,y:e.y+n.spacing,width:d,height:u};l&&(c.options=n),r.updateElement(t[a],a,c,i)}r.updateSharedOptions(h,i,a)}_drawDividers(t,e,n){for(let i=0,r=n.length;i<r;++i){const r=n[i],o=e[i];r.options.groupDividers&&o._data.children.length>1&&D(t,r)}this.getDataset().groupDividers&&D(t,this._rect)}_drawRects(t,e,n,i){for(let r=0,o=n.length;r<o;++r){const o=n[r],s=e[r];if(!o.hidden){o.draw(t);const e=o.options;w(o,e.font)&&s.g&&_(t,o,s,e,i)}}}draw(){const t=this,e=t.chart.ctx,n=t.getMeta().data||[],i=t.getDataset(),r=(i.groups||[]).length-1,o=i.data||[];t._drawRects(e,o,n,r),t._drawDividers(e,o,n)}}function C(t,e){const{x:n,y:i,width:r,height:o}=t.getProps(["x","y","width","height"],e);return{left:n,top:i,right:n+r,bottom:i+o}}function k(t,e,n){return Math.max(Math.min(t,n),e)}function j(t){const e=C(t),n=e.right-e.left,i=e.bottom-e.top,r=function(t,e,n){let i,r,s,a;return o(t)?(i=+t.top||0,r=+t.right||0,s=+t.bottom||0,a=+t.left||0):i=r=s=a=+t||0,{t:k(i,0,n),r:k(r,0,e),b:k(s,0,n),l:k(a,0,e)}}(t.options.borderWidth,n/2,i/2);return{outer:{x:e.left,y:e.top,w:n,h:i},inner:{x:e.left+r.l,y:e.top+r.t,w:n-r.l-r.r,h:i-r.t-r.b}}}function E(t,e,n,i){const r=null===e,o=null===n,s=!(!t||r&&o)&&C(t,i);return s&&(r||e>=s.left&&e<=s.right)&&(o||n>=s.top&&n<=s.bottom)}O.id="treemap",O.defaults={dataElementType:"treemap",groupLabels:!0,borderWidth:0,spacing:.5,groupDividers:!1,dividerWidth:1},O.overrides={interaction:{mode:"point",intersect:!0},hover:{},plugins:{tooltip:{position:"treemap",intersect:!0,callbacks:{title(t){if(t.length){return t[0].dataset.key||""}return""},label(t){const e=t.dataset,n=e.data[t.dataIndex],i=n.g||e.label;return(i?i+": ":"")+n.v}}}},scales:{x:{type:"linear",display:!1},y:{type:"linear",display:!1}}};class S extends e.Element{constructor(t){super(),this.options=void 0,this.width=void 0,this.height=void 0,t&&Object.assign(this,t)}draw(t){const e=this.options,{inner:n,outer:i}=j(this);t.save(),i.w!==n.w||i.h!==n.h?(t.beginPath(),t.rect(i.x,i.y,i.w,i.h),t.clip(),t.rect(n.x,n.y,n.w,n.h),t.fillStyle=e.backgroundColor,t.fill(),t.fillStyle=e.borderColor,t.fill("evenodd")):(t.fillStyle=e.backgroundColor,t.fillRect(n.x,n.y,n.w,n.h)),t.restore()}inRange(t,e,n){return E(this,t,e,n)}inXRange(t,e){return E(this,t,null,e)}inYRange(t,e){return E(this,null,t,e)}getCenterPoint(t){const{x:e,y:n,width:i,height:r}=this.getProps(["x","y","width","height"],t);return{x:e+i/2,y:n+r/2}}tooltipPosition(){return this.getCenterPoint()}getRange(t){return"x"===t?this.width/2:this.height/2}}S.id="treemap",S.defaults={borderSkipped:void 0,borderWidth:void 0,color:void 0,dividerCapStyle:"butt",dividerColor:"black",dividerDash:void 0,dividerDashOffset:0,dividerWidth:0,font:{},groupDividers:!1,groupLabels:void 0,spacing:void 0,label:void 0,rtl:void 0},S.defaultRoutes={backgroundColor:"backgroundColor",borderColor:"borderColor"},e.Chart.register(O,S);e.Chart.registry.plugins.get("tooltip").positioners.treemap=function(t){if(!t.length)return!1;return t[t.length-1].element.tooltipPosition()},t.flatten=i,t.group=r,t.index=s,t.isObject=o,t.sort=a,t.sum=h,Object.defineProperty(t,"__esModule",{value:!0})}));
|
|
7
|
+
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("chart.js"),require("chart.js/helpers")):"function"==typeof define&&define.amd?define(["exports","chart.js","chart.js/helpers"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self)["chartjs-chart-treemap"]={},t.Chart,t.Chart.helpers)}(this,(function(t,e,n){"use strict";function i(t){const e=[...t],n=[];for(;e.length;){const t=e.pop();Array.isArray(t)?e.push(...t):n.push(t)}return n.reverse()}function r(t,e,n,i,r){const o=Object.create(null),s=Object.create(null),a=[];let l,h,u,c;for(h=0,u=t.length;h<u;++h)c=t[h],i&&c[i]!==r||(l=c[e]||"",l in o||(o[l]=0,s[l]=[]),o[l]+=+c[n],s[l].push(c));return Object.keys(o).forEach((t=>{c={children:s[t]},c[n]=+o[t],c[e]=t,i&&(c[i]=r),a.push(c)})),a}function o(t){const e=typeof t;return"function"===e||"object"===e&&!!t}function s(t,e){let n,i=t.length;if(!i)return e;const r=o(t[0]);for(e=r?e:"v",n=0,i=t.length;n<i;++n)r?t[n]._idx=n:t[n]={v:t[n],_idx:n};return e}function a(t,e){e?t.sort(((t,n)=>+n[e]-+t[e])):t.sort(((t,e)=>+e-+t))}function l(t,e){let n,i,r;for(n=0,i=0,r=t.length;i<r;++i)n+=e?+t[i][e]:+t[i];return n}function h(t,e){const n=e.split(".");if(!t.split(".").reduce(((t,e,i)=>t&&e<=n[i]),!0))throw new Error(`Chart.js v${e} is not supported. v${t} or newer is required.`)}function u(t,e){return+(Math.round(t+"e+"+e)+"e-"+e)||0}function c(t,e,n,i){const r=t._normalized,o=e*r/n,s=Math.sqrt(r*o),a=r/s;return{d1:s,d2:a,w:"_ix"===i?s:a,h:"_ix"===i?a:s}}const d=(t,e)=>u(t.rtl?t.x+t.w-t._ix-e:t.x+t._ix,4);function p(t,e,n,i){const r={x:d(t,n.w),y:u(t.y+t._iy,4),w:u(n.w,4),h:u(n.h,4),a:u(e._normalized,4),v:e.value,s:i,_data:e._data};return e.group&&(r.g=e.group,r.l=e.level,r.gs=e.groupSum),r}class g{constructor(t){const e=this;t=t||{w:1,h:1},e.rtl=!!t.rtl,e.x=t.x||t.left||0,e.y=t.y||t.top||0,e._ix=0,e._iy=0,e.w=t.w||t.width||t.right-t.left,e.h=t.h||t.height||t.bottom-t.top}get area(){return this.w*this.h}get iw(){return this.w-this._ix}get ih(){return this.h-this._iy}get dir(){const t=this.ih;return t<=this.iw&&t>0?"y":"x"}get side(){return"x"===this.dir?this.iw:this.ih}map(t){const e=this,n=[],i=t.nsum,r=t.get(),o=e.dir,s=e.side,a=s*s,l="x"===o?"_ix":"_iy",h=i*i;let u=0,d=0;for(const i of r){const r=c(i,a,h,l);d+=r.d1,u=Math.max(u,r.d2),n.push(p(e,i,r,t.sum)),e[l]+=r.d1}return e["y"===o?"_ix":"_iy"]+=u,e[l]-=d,n}}const f=Math.min,m=Math.max;function y(t,e){const n=+e[t.key],i=n*t.ratio;return e._normalized=i,{min:f(t.min,n),max:m(t.max,n),sum:t.sum+n,nmin:f(t.nmin,i),nmax:m(t.nmax,i),nsum:t.nsum+i}}function x(t,e,n){t._arr.push(e),function(t,e){Object.assign(t,e)}(t,n)}class v{constructor(t,e){const n=this;n.key=t,n.ratio=e,n.reset()}get length(){return this._arr.length}reset(){const t=this;t._arr=[],t._hist=[],t.sum=0,t.nsum=0,t.min=1/0,t.max=-1/0,t.nmin=1/0,t.nmax=-1/0}push(t){x(this,t,y(this,t))}pushIf(t,e,...n){const i=y(this,t);if(!e((r=this,{min:r.min,max:r.max,sum:r.sum,nmin:r.nmin,nmax:r.nmax,nsum:r.nsum}),i,n))return t;var r;x(this,t,i)}get(){return this._arr}}function w(t,e,n){if(0===t.sum)return!0;const[i]=n,r=t.nsum*t.nsum,o=e.nsum*e.nsum,s=i*i,a=Math.max(s*t.nmax/r,r/(s*t.nmin));return Math.max(s*e.nmax/o,o/(s*e.nmin))<=a}function b(t,e,n,r,o,h){t=t||[];const u=[],c=new g(e),d=new v("value",c.area/l(t,n));let p=c.side;const f=t.length;let m,y;if(!f)return u;const x=t.slice();n=s(x,n),a(x,n);const b=t=>r&&x[t][r];for(m=0;m<f;++m)y={value:(_=m,n?+x[_][n]:+x[_]),groupSum:h,_data:t[x[m]._idx],level:void 0,group:void 0},r&&(y.level=o,y.group=b(m)),y=d.pushIf(y,w,p),y&&(u.push(c.map(d)),p=c.side,d.reset(),d.push(y));var _;return d.length&&u.push(c.map(d)),i(u)}function _(t,e){if(!e)return!1;const n=t.width||t.w,i=t.height||t.h,r=2*e.lineHeight;return n>r&&i>r}function O(t,e,i,r,o){t.save(),t.beginPath(),t.rect(e.x,e.y,e.width,e.height),t.clip(),"l"in i&&i.l!==o?r.captions&&r.captions.display&&function(t,e,i){const r=i.options,o=r.captions||{},s=r.borderWidth||0,a=n.valueOrDefault(r.spacing,0)+s,l=(i.active?o.hoverColor:o.color)||o.color,h=o.padding,u=o.align||(r.rtl?"right":"left"),c=(i.active?o.hoverFont:o.font)||o.font,d=n.toFont(c),p=D(i,u,h,s);t.fillStyle=l,t.font=d.string,t.textAlign=u,t.textBaseline="middle",t.fillText(o.formatter||e.g,p,i.y+h+a+d.lineHeight/2)}(t,i,e):function(t,e,i){const r=i.options,o=r.labels;if(!o||!o.display)return;const s=(i.active?o.hoverColor:o.color)||o.color,a=(i.active?o.hoverFont:o.font)||o.font,l=n.toFont(a),h=l.lineHeight,u=o.formatter;if(u){const e=n.isArray(u)?u:[u],a=function(t,e,n,i){const r=t.labels,o=t.borderWidth||0,{align:s,position:a,padding:l}=r;let h,u;h=D(e,s,l,o),u="top"===a?e.y+l+o:"bottom"===a?e.y+e.height-l-o-(n.length-1)*i:e.y+e.height/2-n.length*i/4;return{x:h,y:u}}(r,i,e,h);t.font=l.string,t.textAlign=o.align,t.textBaseline=o.position,t.fillStyle=s,e.forEach(((e,n)=>t.fillText(e,a.x,a.y+n*h)))}}(t,0,e),t.restore()}function C(t,e){const n=e.options.dividers||{},i=e.width||e.w,r=e.height||e.h;if(t.save(),t.strokeStyle=n.lineColor||"black",t.lineCap=n.lineCapStyle,t.setLineDash(n.lineDash||[]),t.lineDashOffset=n.lineDashOffset,t.lineWidth=n.lineWidth,t.beginPath(),i>r){const n=i/2;t.moveTo(e.x+n,e.y),t.lineTo(e.x+n,e.y+r)}else{const n=r/2;t.moveTo(e.x,e.y+n),t.lineTo(e.x+i,e.y+n)}t.stroke(),t.restore()}function D(t,e,n,i){return"left"===e?t.x+n+i:"right"===e?t.x+t.width-n-i:t.x+t.width/2}class k extends e.DatasetController{constructor(t,e){super(t,e),this._rect=void 0,this._key=void 0,this._groups=void 0}initialize(){this.enableOptionSharing=!0,super.initialize()}update(t){const e=this,i=e.getMeta(),o=e.getDataset(),s=o.groups||(o.groups=[]),a=o.captions?o.captions:{},l=e.chart.chartArea,h=o.key||"",u=!!o.rtl,c={x:l.left,y:l.top,w:l.right-l.left,h:l.bottom-l.top,rtl:u};var d,p;"reset"!==t&&(d=e._rect,p=c,d&&p&&d.x===p.x&&d.y===p.y&&d.w===p.w&&d.h===p.h)&&e._key===h&&!function(t,e){let n,i;if(t.lenght!==e.length)return!0;for(n=0,i=t.length;n<i;++n)if(t[n]!==e[n])return!0;return!1}(e._groups,s)||(e._rect=c,e._groups=s.slice(),e._key=h,o.data=function(t,e,i){const o=t.key||"";let s=t.tree||[];const a=t.groups||[],l=a.length,h=n.valueOrDefault(t.spacing,0)+n.valueOrDefault(t.borderWidth,0),u=i.font||{},c=n.toFont(u),d=n.valueOrDefault(i.padding,3);return!s.length&&t.data.length&&(s=t.tree=t.data),l?function t(e,u,p,g){const f=a[e],m=e>0&&a[e-1],y=b(r(s,f,o,m,p),u,o,f,e,g),x=y.slice();let v;return e<l-1&&y.forEach((r=>{v={x:r.x+h,y:r.y+h,w:r.w-2*h,h:r.h-2*h},n.valueOrDefault(i.display,!0)&&_(r,c)&&(v.y+=c.lineHeight+2*d,v.h-=c.lineHeight+2*d),x.push(...t(e+1,v,r.g,r.s))})),x}(0,e):b(s,e,o)}(o,c,a),e._dataCheck(),e._resyncElements()),e.updateElements(i.data,0,i.data.length,t)}resolveDataElementOptions(t,e){const i=super.resolveDataElementOptions(t,e),r=Object.isFrozen(i)?Object.assign({},i):i;return r.font=n.toFont(i.captions.font),r}updateElements(t,e,n,i){const r=this,o="reset"===i,s=r.getDataset(),a=r._rect.options=r.resolveDataElementOptions(e,i),l=r.getSharedOptions(a),h=r.includeOptions(i,l);for(let a=e;a<e+n;a++){const e=s.data[a],n=l||r.resolveDataElementOptions(a,i),u=o?0:e.h-2*n.spacing,c=o?0:e.w-2*n.spacing,d={x:e.x+n.spacing,y:e.y+n.spacing,width:c,height:u};h&&(d.options=n),r.updateElement(t[a],a,d,i)}r.updateSharedOptions(l,i,a)}_drawDividers(t,e,n){for(let i=0,r=n.length;i<r;++i){const r=n[i],o=e[i];(r.options.dividers||{}).display&&o._data.children.length>1&&C(t,r)}}_drawRects(t,e,n,i){for(let r=0,o=n.length;r<o;++r){const o=n[r],s=e[r];if(!o.hidden){o.draw(t);const e=o.options;_(o,e.captions.font)&&O(t,o,s,e,i)}}}draw(){const t=this,e=t.chart.ctx,n=t.getMeta().data||[],i=t.getDataset(),r=(i.groups||[]).length-1,o=i.data||[];t._drawRects(e,o,n,r),t._drawDividers(e,o,n)}}function j(t,e){const{x:n,y:i,width:r,height:o}=t.getProps(["x","y","width","height"],e);return{left:n,top:i,right:n+r,bottom:i+o}}function E(t,e,n){return Math.max(Math.min(t,n),e)}function S(t){const e=j(t),n=e.right-e.left,i=e.bottom-e.top,r=function(t,e,n){let i,r,s,a;return o(t)?(i=+t.top||0,r=+t.right||0,s=+t.bottom||0,a=+t.left||0):i=r=s=a=+t||0,{t:E(i,0,n),r:E(r,0,e),b:E(s,0,n),l:E(a,0,e)}}(t.options.borderWidth,n/2,i/2);return{outer:{x:e.left,y:e.top,w:n,h:i},inner:{x:e.left+r.l,y:e.top+r.t,w:n-r.l-r.r,h:i-r.t-r.b}}}function M(t,e,n,i){const r=null===e,o=null===n,s=!(!t||r&&o)&&j(t,i);return s&&(r||e>=s.left&&e<=s.right)&&(o||n>=s.top&&n<=s.bottom)}k.id="treemap",k.version="1.0.3",k.defaults={dataElementType:"treemap",borderWidth:0,spacing:.5,dividers:{display:!1,lineWidth:1}},k.descriptors={_scriptable:!0,_indexable:!1},k.overrides={interaction:{mode:"point",intersect:!0},hover:{},plugins:{tooltip:{position:"treemap",intersect:!0,callbacks:{title(t){if(t.length){return t[0].dataset.key||""}return""},label(t){const e=t.dataset,n=e.data[t.dataIndex],i=n.g||e.label;return(i?i+": ":"")+n.v}}}},scales:{x:{type:"linear",display:!1},y:{type:"linear",display:!1}}},k.beforeRegister=function(){h("3.6",e.Chart.version)},k.afterRegister=function(){const t=e.registry.plugins.get("tooltip");t&&(t.positioners.treemap=function(t){if(!t.length)return!1;return t[t.length-1].element.tooltipPosition()})},k.afterUnregister=function(){const t=e.registry.plugins.get("tooltip");t&&delete t.positioners.treemap};class P extends e.Element{constructor(t){super(),this.options=void 0,this.width=void 0,this.height=void 0,t&&Object.assign(this,t)}draw(t){const e=this.options,{inner:n,outer:i}=S(this);t.save(),i.w!==n.w||i.h!==n.h?(t.beginPath(),t.rect(i.x,i.y,i.w,i.h),t.clip(),t.rect(n.x,n.y,n.w,n.h),t.fillStyle=e.backgroundColor,t.fill(),t.fillStyle=e.borderColor,t.fill("evenodd")):(t.fillStyle=e.backgroundColor,t.fillRect(n.x,n.y,n.w,n.h)),t.restore()}inRange(t,e,n){return M(this,t,e,n)}inXRange(t,e){return M(this,t,null,e)}inYRange(t,e){return M(this,null,t,e)}getCenterPoint(t){const{x:e,y:n,width:i,height:r}=this.getProps(["x","y","width","height"],t);return{x:e+i/2,y:n+r/2}}tooltipPosition(){return this.getCenterPoint()}getRange(t){return"x"===t?this.width/2:this.height/2}}P.id="treemap",P.defaults={borderWidth:void 0,spacing:void 0,label:void 0,rtl:void 0,dividers:{display:!1,lineCapStyle:"butt",lineColor:"black",lineDash:void 0,lineDashOffset:0,lineWidth:0},captions:{align:void 0,color:void 0,display:!0,formatter:t=>t.raw.g||"",font:{},padding:3},labels:{align:"center",color:void 0,display:!1,formatter:t=>t.raw.g?[t.raw.g,t.raw.v]:t.raw.v,font:{},position:"middle",padding:3}},P.descriptors={_scriptable:!0,_indexable:!1},P.defaultRoutes={backgroundColor:"backgroundColor",borderColor:"borderColor"},e.Chart.register(k,P),t.flatten=i,t.group=r,t.index=s,t.isObject=o,t.requireVersion=h,t.sort=a,t.sum=l,Object.defineProperty(t,"__esModule",{value:!0})}));
|
package/package.json
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chartjs-chart-treemap",
|
|
3
|
-
"
|
|
3
|
+
"homepage": "https://chartjs-chart-treemap.pages.dev/",
|
|
4
|
+
"version": "1.0.3",
|
|
4
5
|
"description": "Chart.js module for creating treemap charts",
|
|
5
6
|
"main": "dist/chartjs-chart-treemap.js",
|
|
6
7
|
"module": "dist/chartjs-chart-treemap.esm.js",
|
|
8
|
+
"types": "types/index.esm.d.ts",
|
|
7
9
|
"scripts": {
|
|
8
10
|
"autobuild": "rollup -c -w",
|
|
9
11
|
"build": "rollup -c",
|
|
10
12
|
"dev": "karma start --no-signle-run --auto-watch --browsers chrome",
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
+
"docs": "npm run build && vuepress build docs --no-cache",
|
|
14
|
+
"docs:dev": "npm run build && vuepress dev docs --no-cache",
|
|
15
|
+
"lint": "concurrently -r \"npm:lint-*\"",
|
|
16
|
+
"lint-js": "eslint \"src/**/*.js\" \"test/**/*.js\" \"docs/**/*.js\"",
|
|
17
|
+
"lint-md": "eslint \"**/*.md\"",
|
|
18
|
+
"lint-types": "eslint \"types/**/*.ts\" && tsc -p types/tests/",
|
|
13
19
|
"test": "cross-env NODE_ENV=test concurrently \"npm:test-*\"",
|
|
14
20
|
"test-lint": "npm run lint",
|
|
15
21
|
"test-karma": "karma start --auto-watch --single-run"
|
|
@@ -24,29 +30,33 @@
|
|
|
24
30
|
"treemap"
|
|
25
31
|
],
|
|
26
32
|
"files": [
|
|
27
|
-
"dist/*.js"
|
|
33
|
+
"dist/*.js",
|
|
34
|
+
"types/index.esm.d.ts"
|
|
28
35
|
],
|
|
29
36
|
"author": "Jukka Kurkela",
|
|
30
37
|
"license": "MIT",
|
|
31
38
|
"bugs": {
|
|
32
39
|
"url": "https://github.com/kurkle/chartjs-chart-treemap/issues"
|
|
33
40
|
},
|
|
34
|
-
"homepage": "https://github.com/kurkle/chartjs-chart-treemap#readme",
|
|
35
41
|
"devDependencies": {
|
|
36
|
-
"@rollup/plugin-commonjs": "^
|
|
37
|
-
"@rollup/plugin-
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
+
"@rollup/plugin-commonjs": "^19.0.0",
|
|
43
|
+
"@rollup/plugin-json": "^4.1.0",
|
|
44
|
+
"@rollup/plugin-node-resolve": "^13.0.0",
|
|
45
|
+
"@typescript-eslint/eslint-plugin": "^4.22.0",
|
|
46
|
+
"@typescript-eslint/parser": "^4.22.0",
|
|
47
|
+
"chart.js": "^3.6.0",
|
|
48
|
+
"chartjs-adapter-date-fns": "^2.0.0",
|
|
49
|
+
"chartjs-test-utils": "^0.3.0",
|
|
50
|
+
"concurrently": "^6.0.1",
|
|
42
51
|
"cross-env": "^7.0.3",
|
|
43
|
-
"date-fns": "^2.
|
|
44
|
-
"eslint": "^7.
|
|
52
|
+
"date-fns": "^2.20.2",
|
|
53
|
+
"eslint": "^7.24.0",
|
|
45
54
|
"eslint-config-chartjs": "^0.3.0",
|
|
46
55
|
"eslint-plugin-es": "^4.1.0",
|
|
47
56
|
"eslint-plugin-html": "^6.1.2",
|
|
57
|
+
"eslint-plugin-markdown": "^2.0.1",
|
|
48
58
|
"jasmine-core": "^3.7.1",
|
|
49
|
-
"karma": "^6.2
|
|
59
|
+
"karma": "^6.3.2",
|
|
50
60
|
"karma-chrome-launcher": "^3.1.0",
|
|
51
61
|
"karma-coverage": "^2.0.3",
|
|
52
62
|
"karma-firefox-launcher": "^2.1.0",
|
|
@@ -56,12 +66,17 @@
|
|
|
56
66
|
"karma-spec-reporter": "0.0.32",
|
|
57
67
|
"karma-summary-reporter": "^2.0.2",
|
|
58
68
|
"pixelmatch": "^5.2.1",
|
|
59
|
-
"rollup": "^2.
|
|
69
|
+
"rollup": "^2.45.1",
|
|
70
|
+
"rollup-plugin-analyzer": "^4.0.0",
|
|
60
71
|
"rollup-plugin-istanbul": "^3.0.0",
|
|
61
|
-
"rollup-plugin-terser": "^7.0.2"
|
|
72
|
+
"rollup-plugin-terser": "^7.0.2",
|
|
73
|
+
"typescript": "^4.3.5",
|
|
74
|
+
"vuepress": "^1.8.2",
|
|
75
|
+
"vuepress-plugin-flexsearch": "^0.2.0",
|
|
76
|
+
"vuepress-plugin-redirect": "^1.2.5",
|
|
77
|
+
"vuepress-theme-chartjs": "^0.2.0"
|
|
62
78
|
},
|
|
63
79
|
"peerDependencies": {
|
|
64
|
-
"chart.js": "^3.0.0
|
|
65
|
-
}
|
|
66
|
-
"dependencies": {}
|
|
80
|
+
"chart.js": "^3.0.0"
|
|
81
|
+
}
|
|
67
82
|
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Chart,
|
|
3
|
+
ChartComponent,
|
|
4
|
+
DatasetController,
|
|
5
|
+
Element,
|
|
6
|
+
ScriptableContext, Color, Scriptable, FontSpec
|
|
7
|
+
} from 'chart.js';
|
|
8
|
+
|
|
9
|
+
type TreemapScriptableContext = ScriptableContext<'treemap'> & {
|
|
10
|
+
raw: TreemapDataPoint
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type TreemapControllerDatasetCaptionsOptions = {
|
|
14
|
+
align?: Scriptable<LabelAlign, TreemapScriptableContext>,
|
|
15
|
+
color?: Scriptable<Color, TreemapScriptableContext>,
|
|
16
|
+
display?: boolean;
|
|
17
|
+
formatter?: Scriptable<string, TreemapScriptableContext>,
|
|
18
|
+
font?: FontSpec,
|
|
19
|
+
hoverColor?: Scriptable<Color, TreemapScriptableContext>,
|
|
20
|
+
hoverFont?: FontSpec,
|
|
21
|
+
padding?: number,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
type TreemapControllerDatasetLabelsOptions = {
|
|
25
|
+
align?: Scriptable<LabelAlign, TreemapScriptableContext>,
|
|
26
|
+
color?: Scriptable<Color, TreemapScriptableContext>,
|
|
27
|
+
display?: boolean;
|
|
28
|
+
formatter?: Scriptable<string | Array<string>, TreemapScriptableContext>,
|
|
29
|
+
font?: FontSpec,
|
|
30
|
+
hoverColor?: Scriptable<Color, TreemapScriptableContext>,
|
|
31
|
+
hoverFont?: FontSpec,
|
|
32
|
+
padding?: number,
|
|
33
|
+
position?: Scriptable<LabelPosition, TreemapScriptableContext>
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type LabelPosition = 'top' | 'middle' | 'bottom';
|
|
37
|
+
|
|
38
|
+
export type LabelAlign = 'left' | 'center' | 'right';
|
|
39
|
+
|
|
40
|
+
type TreemapControllerDatasetDividersOptions = {
|
|
41
|
+
display?: boolean,
|
|
42
|
+
lineCapStyle?: string,
|
|
43
|
+
lineColor?: string,
|
|
44
|
+
lineDash?: number[],
|
|
45
|
+
lineDashOffset?: number,
|
|
46
|
+
lineWidth?: number,
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface TreemapControllerDatasetOptions<DType> {
|
|
50
|
+
spacing?: number,
|
|
51
|
+
rtl?: boolean,
|
|
52
|
+
|
|
53
|
+
backgroundColor?: Scriptable<Color, TreemapScriptableContext>;
|
|
54
|
+
borderColor?: Scriptable<Color, TreemapScriptableContext>;
|
|
55
|
+
borderWidth?: number;
|
|
56
|
+
|
|
57
|
+
hoverBackgroundColor?: Scriptable<Color, TreemapScriptableContext>;
|
|
58
|
+
hoverBorderColor?: Scriptable<Color, TreemapScriptableContext>;
|
|
59
|
+
hoverBorderWidth?: number;
|
|
60
|
+
|
|
61
|
+
captions?: TreemapControllerDatasetCaptionsOptions;
|
|
62
|
+
dividers?: TreemapControllerDatasetDividersOptions;
|
|
63
|
+
labels?: TreemapControllerDatasetLabelsOptions;
|
|
64
|
+
label?: string;
|
|
65
|
+
|
|
66
|
+
data: TreemapDataPoint[]; // This will be auto-generated from `tree`
|
|
67
|
+
groups?: Array<keyof DType>;
|
|
68
|
+
tree: number[] | DType[];
|
|
69
|
+
key?: keyof DType;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface TreemapDataPoint {
|
|
73
|
+
x: number,
|
|
74
|
+
y: number,
|
|
75
|
+
w: number,
|
|
76
|
+
h: number,
|
|
77
|
+
/**
|
|
78
|
+
* Value
|
|
79
|
+
*/
|
|
80
|
+
v: number,
|
|
81
|
+
/**
|
|
82
|
+
* Sum
|
|
83
|
+
*/
|
|
84
|
+
s: number,
|
|
85
|
+
/**
|
|
86
|
+
* Depth, only available if grouping
|
|
87
|
+
*/
|
|
88
|
+
l?: number,
|
|
89
|
+
/**
|
|
90
|
+
* Group name, only available if grouping
|
|
91
|
+
*/
|
|
92
|
+
g?: string,
|
|
93
|
+
/**
|
|
94
|
+
* Group Sum, only available if grouping
|
|
95
|
+
*/
|
|
96
|
+
gs?: number,
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/*
|
|
100
|
+
export interface TreemapInteractionOptions {
|
|
101
|
+
position: Scriptable<"treemap", ScriptableTooltipContext<"treemap">>
|
|
102
|
+
}*/
|
|
103
|
+
|
|
104
|
+
declare module 'chart.js' {
|
|
105
|
+
export interface ChartTypeRegistry {
|
|
106
|
+
treemap: {
|
|
107
|
+
chartOptions: CoreChartOptions<'treemap'>;
|
|
108
|
+
datasetOptions: TreemapControllerDatasetOptions<Record<string, unknown>>;
|
|
109
|
+
defaultDataPoint: TreemapDataPoint;
|
|
110
|
+
metaExtensions: {};
|
|
111
|
+
parsedDataType: unknown,
|
|
112
|
+
scales: never;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// interface TooltipOptions<TType extends ChartType> extends CoreInteractionOptions, TreemapInteractionOptions {
|
|
117
|
+
// }
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
type TreemapOptions = {
|
|
121
|
+
backgroundColor: Color;
|
|
122
|
+
borderColor: Color;
|
|
123
|
+
borderWidth: number | { top?: number, right?: number, bottom?: number, left?: number }
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
type TreemapConfig = {
|
|
127
|
+
x: number;
|
|
128
|
+
y: number;
|
|
129
|
+
width: number;
|
|
130
|
+
height: number;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export type TreemapController = DatasetController;
|
|
134
|
+
export const TreemapController: ChartComponent & {
|
|
135
|
+
prototype: TreemapController;
|
|
136
|
+
new(chart: Chart, datasetIndex: number): TreemapController
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
export type TreemapElement = Element<TreemapConfig, TreemapOptions>;
|
|
140
|
+
export const TreemapElement: ChartComponent & {
|
|
141
|
+
prototype: TreemapElement;
|
|
142
|
+
new(cfg: TreemapConfig): TreemapElement
|
|
143
|
+
};
|