chartjs-chart-treemap 1.0.2 → 2.0.2

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/README.md CHANGED
@@ -1,16 +1,20 @@
1
1
  # chartjs-chart-treemap
2
2
 
3
- [Chart.js](https://www.chartjs.org/) **v3.0.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)
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
5
  [![npm](https://img.shields.io/npm/v/chartjs-chart-treemap.svg)](https://www.npmjs.com/package/chartjs-chart-treemap)
6
6
  [![release](https://img.shields.io/github/release/kurkle/chartjs-chart-treemap.svg?style=flat-square)](https://github.com/kurkle/chartjs-chart-treemap/releases/latest)
7
7
  ![npm bundle size](https://img.shields.io/bundlephobia/min/chartjs-chart-treemap.svg)
8
- [![documentation](https://img.shields.io/static/v1?message=Documentation&color=informational)](https://chartjs-chart-treemap.pages.dev)
9
8
  ![GitHub](https://img.shields.io/github/license/kurkle/chartjs-chart-treemap.svg)
10
- [![Coverage Status](https://coveralls.io/repos/github/kurkle/chartjs-chart-treemap/badge.svg)](https://coveralls.io/github/kurkle/chartjs-chart-treemap)
9
+ [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=kurkle_chartjs-chart-treemap&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=kurkle_chartjs-chart-treemap)
10
+ [![documentation](https://img.shields.io/static/v1?message=Documentation&color=informational)](https://chartjs-chart-treemap.pages.dev)
11
11
 
12
12
  ![TreeMap Example Image](treemap.png)
13
13
 
14
+ ## Documentation
15
+
16
+ You can find documentation for chartjs-chart-treemap at [https://chartjs-chart-treemap.pages.dev/](https://chartjs-chart-treemap.pages.dev/).
17
+
14
18
  ## Development
15
19
 
16
20
  You first need to install node dependencies (requires [Node.js](https://nodejs.org/)):
@@ -1,11 +1,11 @@
1
1
  /*!
2
- * chartjs-chart-treemap v1.0.2
2
+ * chartjs-chart-treemap v2.0.2
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 { registry, 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,6 +108,13 @@ 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) {
112
119
  // @ts-ignore
113
120
  return (+(Math.round(v + 'e+' + n) + 'e-' + n)) || 0;
@@ -346,7 +353,7 @@ function squarify(values, rectangle, key, grp, lvl, gsum) {
346
353
  return flatten(rows);
347
354
  }
348
355
 
349
- var version = "1.0.2";
356
+ var version = "2.0.2";
350
357
 
351
358
  function rectNotEqual(r1, r2) {
352
359
  return !r1 || !r2
@@ -383,35 +390,47 @@ function shouldDrawCaption(rect, font) {
383
390
 
384
391
  function drawCaption(ctx, rect, item, opts, levels) {
385
392
  ctx.save();
386
- ctx.fillStyle = opts.color;
387
- ctx.font = opts.font.string;
388
393
  ctx.beginPath();
389
394
  ctx.rect(rect.x, rect.y, rect.width, rect.height);
390
395
  ctx.clip();
391
396
  if (!('l' in item) || item.l === levels) {
392
- ctx.textAlign = 'center';
393
- ctx.textBaseline = 'middle';
394
397
  drawLabels(ctx, item, rect);
395
- } else if (opts.groupLabels) {
396
- ctx.textAlign = opts.rtl ? 'end' : 'start';
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);
398
+ } else if (opts.captions && opts.captions.display) {
399
+ drawCaptionLabel(ctx, item, rect);
400
400
  }
401
401
  ctx.restore();
402
402
  }
403
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
+
404
422
  function drawDivider(ctx, rect) {
405
423
  const opts = rect.options;
424
+ const dividersOpts = opts.dividers || {};
406
425
  const w = rect.width || rect.w;
407
426
  const h = rect.height || rect.h;
408
427
 
409
428
  ctx.save();
410
- ctx.strokeStyle = opts.dividerColor || 'black';
411
- ctx.lineCap = opts.dividerCapStyle;
412
- ctx.setLineDash(opts.dividerDash || []);
413
- ctx.lineDashOffset = opts.dividerDashOffset;
414
- ctx.lineWidth = opts.dividerWidth;
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;
415
434
  ctx.beginPath();
416
435
  if (w > h) {
417
436
  const w2 = w / 2;
@@ -426,12 +445,15 @@ function drawDivider(ctx, rect) {
426
445
  ctx.restore();
427
446
  }
428
447
 
429
- function buildData(dataset, mainRect, font) {
448
+ function buildData(dataset, mainRect, captions) {
430
449
  const key = dataset.key || '';
431
450
  let tree = dataset.tree || [];
432
451
  const groups = dataset.groups || [];
433
452
  const glen = groups.length;
434
- const sp = (dataset.spacing || 0) + (dataset.borderWidth || 0);
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);
435
457
 
436
458
  function recur(gidx, rect, parent, gs) {
437
459
  const g = groups[gidx];
@@ -443,10 +465,9 @@ function buildData(dataset, mainRect, font) {
443
465
  if (gidx < glen - 1) {
444
466
  gsq.forEach((sq) => {
445
467
  subRect = {x: sq.x + sp, y: sq.y + sp, w: sq.w - 2 * sp, h: sq.h - 2 * sp};
446
-
447
- if (valueOrDefault(dataset.groupLabels, true) && shouldDrawCaption(sq, font)) {
448
- subRect.y += font.lineHeight;
449
- 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;
450
471
  }
451
472
  ret.push(...recur(gidx + 1, subRect, sq.g, sq.s));
452
473
  });
@@ -465,10 +486,49 @@ function buildData(dataset, mainRect, font) {
465
486
 
466
487
  function drawLabels(ctx, item, rect) {
467
488
  const opts = rect.options;
468
- const lh = opts.font.lineHeight;
469
- const labels = (opts.label || item.g + '\n' + item.v).split('\n');
470
- const y = rect.y + rect.height / 2 - labels.length * lh / 4;
471
- labels.forEach((l, i) => ctx.fillText(l, rect.x + rect.width / 2, y + i * lh));
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;
472
532
  }
473
533
 
474
534
  class TreemapController extends DatasetController {
@@ -490,7 +550,7 @@ class TreemapController extends DatasetController {
490
550
  const meta = me.getMeta();
491
551
  const dataset = me.getDataset();
492
552
  const groups = dataset.groups || (dataset.groups = []);
493
- const font = toFont(dataset.font);
553
+ const captions = dataset.captions ? dataset.captions : {};
494
554
  const area = me.chart.chartArea;
495
555
  const key = dataset.key || '';
496
556
  const rtl = !!dataset.rtl;
@@ -501,7 +561,8 @@ class TreemapController extends DatasetController {
501
561
  me._rect = mainRect;
502
562
  me._groups = groups.slice();
503
563
  me._key = key;
504
- dataset.data = buildData(dataset, mainRect, font);
564
+
565
+ dataset.data = buildData(dataset, mainRect, captions);
505
566
  // @ts-ignore using private stuff
506
567
  me._dataCheck();
507
568
  // @ts-ignore using private stuff
@@ -514,7 +575,7 @@ class TreemapController extends DatasetController {
514
575
  resolveDataElementOptions(index, mode) {
515
576
  const options = super.resolveDataElementOptions(index, mode);
516
577
  const result = Object.isFrozen(options) ? Object.assign({}, options) : options;
517
- result.font = toFont(options.font);
578
+ result.font = toFont(options.captions.font);
518
579
  return result;
519
580
  }
520
581
 
@@ -529,15 +590,14 @@ class TreemapController extends DatasetController {
529
590
  for (let i = start; i < start + count; i++) {
530
591
  const sq = dataset.data[i];
531
592
  const options = sharedOptions || me.resolveDataElementOptions(i, mode);
532
- const height = reset ? 0 : sq.h - options.spacing * 2;
533
- const width = reset ? 0 : sq.w - options.spacing * 2;
534
- const x = sq.x + options.spacing;
535
- const y = sq.y + options.spacing;
593
+ const sp = options.spacing;
594
+ const sp2 = sp * 2;
536
595
  const properties = {
537
- x,
538
- y,
539
- width,
540
- height
596
+ x: sq.x + sp,
597
+ y: sq.y + sp,
598
+ width: reset ? 0 : sq.w - sp2,
599
+ height: reset ? 0 : sq.h - sp2,
600
+ hidden: sp2 > sq.w || sp2 > sq.h,
541
601
  };
542
602
 
543
603
  if (includeOptions) {
@@ -553,13 +613,11 @@ class TreemapController extends DatasetController {
553
613
  for (let i = 0, ilen = metadata.length; i < ilen; ++i) {
554
614
  const rect = metadata[i];
555
615
  const item = data[i];
556
- if (rect.options.groupDividers && item._data.children.length > 1) {
616
+ const dividersOpts = rect.options.dividers || {};
617
+ if (dividersOpts.display && item._data.children.length > 1) {
557
618
  drawDivider(ctx, rect);
558
619
  }
559
620
  }
560
- if (this.getDataset().groupDividers) {
561
- drawDivider(ctx, this._rect);
562
- }
563
621
  }
564
622
 
565
623
  _drawRects(ctx, data, metadata, levels) {
@@ -569,7 +627,7 @@ class TreemapController extends DatasetController {
569
627
  if (!rect.hidden) {
570
628
  rect.draw(ctx);
571
629
  const opts = rect.options;
572
- if (shouldDrawCaption(rect, opts.font) && item.g) {
630
+ if (shouldDrawCaption(rect, opts.captions.font)) {
573
631
  drawCaption(ctx, rect, item, opts, levels);
574
632
  }
575
633
  }
@@ -596,12 +654,25 @@ TreemapController.version = version;
596
654
  TreemapController.defaults = {
597
655
  dataElementType: 'treemap',
598
656
 
599
- groupLabels: true,
657
+ animations: {
658
+ numbers: {
659
+ type: 'number',
660
+ properties: ['x', 'y', 'width', 'height']
661
+ },
662
+ },
663
+
600
664
  borderWidth: 0,
601
665
  spacing: 0.5,
602
- groupDividers: false,
603
- dividerWidth: 1
666
+ dividers: {
667
+ display: false,
668
+ lineWidth: 1,
669
+ }
670
+
671
+ };
604
672
 
673
+ TreemapController.descriptors = {
674
+ _scriptable: true,
675
+ _indexable: false
605
676
  };
606
677
 
607
678
  TreemapController.overrides = {
@@ -645,6 +716,10 @@ TreemapController.overrides = {
645
716
  },
646
717
  };
647
718
 
719
+ TreemapController.beforeRegister = function() {
720
+ requireVersion('3.6', Chart.version);
721
+ };
722
+
648
723
  TreemapController.afterRegister = function() {
649
724
  const tooltipPlugin = registry.plugins.get('tooltip');
650
725
  if (tooltipPlugin) {
@@ -769,7 +844,6 @@ class TreemapElement extends Element {
769
844
  ctx.fillStyle = options.backgroundColor;
770
845
  ctx.fillRect(inner.x, inner.y, inner.w, inner.h);
771
846
  }
772
-
773
847
  ctx.restore();
774
848
  }
775
849
 
@@ -805,20 +879,40 @@ class TreemapElement extends Element {
805
879
  TreemapElement.id = 'treemap';
806
880
 
807
881
  TreemapElement.defaults = {
808
- borderSkipped: undefined,
809
882
  borderWidth: undefined,
810
- color: undefined,
811
- dividerCapStyle: 'butt',
812
- dividerColor: 'black',
813
- dividerDash: undefined,
814
- dividerDashOffset: 0,
815
- dividerWidth: 0,
816
- font: {},
817
- groupDividers: false,
818
- groupLabels: undefined,
819
883
  spacing: undefined,
820
884
  label: undefined,
821
- rtl: undefined
885
+ rtl: undefined,
886
+ dividers: {
887
+ display: false,
888
+ lineCapStyle: 'butt',
889
+ lineColor: 'black',
890
+ lineDash: undefined,
891
+ lineDashOffset: 0,
892
+ lineWidth: 0,
893
+ },
894
+ captions: {
895
+ align: undefined,
896
+ color: undefined,
897
+ display: true,
898
+ formatter: (ctx) => ctx.raw.g || '',
899
+ font: {},
900
+ padding: 3
901
+ },
902
+ labels: {
903
+ align: 'center',
904
+ color: undefined,
905
+ display: false,
906
+ formatter: (ctx) => ctx.raw.g ? [ctx.raw.g, ctx.raw.v] : ctx.raw.v,
907
+ font: {},
908
+ position: 'middle',
909
+ padding: 3
910
+ }
911
+ };
912
+
913
+ TreemapElement.descriptors = {
914
+ _scriptable: true,
915
+ _indexable: false
822
916
  };
823
917
 
824
918
  TreemapElement.defaultRoutes = {
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * chartjs-chart-treemap v1.0.2
2
+ * chartjs-chart-treemap v2.0.2
3
3
  * https://chartjs-chart-treemap.pages.dev/
4
4
  * (c) 2021 Jukka Kurkela
5
5
  * Released under the MIT license
@@ -7,8 +7,8 @@
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['chartjs-chart-treemap'] = {}, global.Chart, global.Chart.helpers));
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,6 +111,13 @@ 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) {
115
122
  // @ts-ignore
116
123
  return (+(Math.round(v + 'e+' + n) + 'e-' + n)) || 0;
@@ -349,7 +356,7 @@ function squarify(values, rectangle, key, grp, lvl, gsum) {
349
356
  return flatten(rows);
350
357
  }
351
358
 
352
- var version = "1.0.2";
359
+ var version = "2.0.2";
353
360
 
354
361
  function rectNotEqual(r1, r2) {
355
362
  return !r1 || !r2
@@ -386,35 +393,47 @@ function shouldDrawCaption(rect, font) {
386
393
 
387
394
  function drawCaption(ctx, rect, item, opts, levels) {
388
395
  ctx.save();
389
- ctx.fillStyle = opts.color;
390
- ctx.font = opts.font.string;
391
396
  ctx.beginPath();
392
397
  ctx.rect(rect.x, rect.y, rect.width, rect.height);
393
398
  ctx.clip();
394
399
  if (!('l' in item) || item.l === levels) {
395
- ctx.textAlign = 'center';
396
- ctx.textBaseline = 'middle';
397
400
  drawLabels(ctx, item, rect);
398
- } else if (opts.groupLabels) {
399
- ctx.textAlign = opts.rtl ? 'end' : 'start';
400
- ctx.textBaseline = 'top';
401
- const x = opts.rtl ? rect.x + rect.width - opts.borderWidth - 3 : rect.x + opts.borderWidth + 3;
402
- ctx.fillText(item.g, x, rect.y + opts.borderWidth + 3);
401
+ } else if (opts.captions && opts.captions.display) {
402
+ drawCaptionLabel(ctx, item, rect);
403
403
  }
404
404
  ctx.restore();
405
405
  }
406
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
+
407
425
  function drawDivider(ctx, rect) {
408
426
  const opts = rect.options;
427
+ const dividersOpts = opts.dividers || {};
409
428
  const w = rect.width || rect.w;
410
429
  const h = rect.height || rect.h;
411
430
 
412
431
  ctx.save();
413
- ctx.strokeStyle = opts.dividerColor || 'black';
414
- ctx.lineCap = opts.dividerCapStyle;
415
- ctx.setLineDash(opts.dividerDash || []);
416
- ctx.lineDashOffset = opts.dividerDashOffset;
417
- ctx.lineWidth = opts.dividerWidth;
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;
418
437
  ctx.beginPath();
419
438
  if (w > h) {
420
439
  const w2 = w / 2;
@@ -429,12 +448,15 @@ function drawDivider(ctx, rect) {
429
448
  ctx.restore();
430
449
  }
431
450
 
432
- function buildData(dataset, mainRect, font) {
451
+ function buildData(dataset, mainRect, captions) {
433
452
  const key = dataset.key || '';
434
453
  let tree = dataset.tree || [];
435
454
  const groups = dataset.groups || [];
436
455
  const glen = groups.length;
437
- const sp = (dataset.spacing || 0) + (dataset.borderWidth || 0);
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);
438
460
 
439
461
  function recur(gidx, rect, parent, gs) {
440
462
  const g = groups[gidx];
@@ -446,10 +468,9 @@ function buildData(dataset, mainRect, font) {
446
468
  if (gidx < glen - 1) {
447
469
  gsq.forEach((sq) => {
448
470
  subRect = {x: sq.x + sp, y: sq.y + sp, w: sq.w - 2 * sp, h: sq.h - 2 * sp};
449
-
450
- if (helpers.valueOrDefault(dataset.groupLabels, true) && shouldDrawCaption(sq, font)) {
451
- subRect.y += font.lineHeight;
452
- 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;
453
474
  }
454
475
  ret.push(...recur(gidx + 1, subRect, sq.g, sq.s));
455
476
  });
@@ -468,10 +489,49 @@ function buildData(dataset, mainRect, font) {
468
489
 
469
490
  function drawLabels(ctx, item, rect) {
470
491
  const opts = rect.options;
471
- const lh = opts.font.lineHeight;
472
- const labels = (opts.label || item.g + '\n' + item.v).split('\n');
473
- const y = rect.y + rect.height / 2 - labels.length * lh / 4;
474
- labels.forEach((l, i) => ctx.fillText(l, rect.x + rect.width / 2, y + i * lh));
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;
475
535
  }
476
536
 
477
537
  class TreemapController extends chart_js.DatasetController {
@@ -493,7 +553,7 @@ class TreemapController extends chart_js.DatasetController {
493
553
  const meta = me.getMeta();
494
554
  const dataset = me.getDataset();
495
555
  const groups = dataset.groups || (dataset.groups = []);
496
- const font = helpers.toFont(dataset.font);
556
+ const captions = dataset.captions ? dataset.captions : {};
497
557
  const area = me.chart.chartArea;
498
558
  const key = dataset.key || '';
499
559
  const rtl = !!dataset.rtl;
@@ -504,7 +564,8 @@ class TreemapController extends chart_js.DatasetController {
504
564
  me._rect = mainRect;
505
565
  me._groups = groups.slice();
506
566
  me._key = key;
507
- dataset.data = buildData(dataset, mainRect, font);
567
+
568
+ dataset.data = buildData(dataset, mainRect, captions);
508
569
  // @ts-ignore using private stuff
509
570
  me._dataCheck();
510
571
  // @ts-ignore using private stuff
@@ -517,7 +578,7 @@ class TreemapController extends chart_js.DatasetController {
517
578
  resolveDataElementOptions(index, mode) {
518
579
  const options = super.resolveDataElementOptions(index, mode);
519
580
  const result = Object.isFrozen(options) ? Object.assign({}, options) : options;
520
- result.font = helpers.toFont(options.font);
581
+ result.font = helpers.toFont(options.captions.font);
521
582
  return result;
522
583
  }
523
584
 
@@ -532,15 +593,14 @@ class TreemapController extends chart_js.DatasetController {
532
593
  for (let i = start; i < start + count; i++) {
533
594
  const sq = dataset.data[i];
534
595
  const options = sharedOptions || me.resolveDataElementOptions(i, mode);
535
- const height = reset ? 0 : sq.h - options.spacing * 2;
536
- const width = reset ? 0 : sq.w - options.spacing * 2;
537
- const x = sq.x + options.spacing;
538
- const y = sq.y + options.spacing;
596
+ const sp = options.spacing;
597
+ const sp2 = sp * 2;
539
598
  const properties = {
540
- x,
541
- y,
542
- width,
543
- height
599
+ x: sq.x + sp,
600
+ y: sq.y + sp,
601
+ width: reset ? 0 : sq.w - sp2,
602
+ height: reset ? 0 : sq.h - sp2,
603
+ hidden: sp2 > sq.w || sp2 > sq.h,
544
604
  };
545
605
 
546
606
  if (includeOptions) {
@@ -556,13 +616,11 @@ class TreemapController extends chart_js.DatasetController {
556
616
  for (let i = 0, ilen = metadata.length; i < ilen; ++i) {
557
617
  const rect = metadata[i];
558
618
  const item = data[i];
559
- if (rect.options.groupDividers && item._data.children.length > 1) {
619
+ const dividersOpts = rect.options.dividers || {};
620
+ if (dividersOpts.display && item._data.children.length > 1) {
560
621
  drawDivider(ctx, rect);
561
622
  }
562
623
  }
563
- if (this.getDataset().groupDividers) {
564
- drawDivider(ctx, this._rect);
565
- }
566
624
  }
567
625
 
568
626
  _drawRects(ctx, data, metadata, levels) {
@@ -572,7 +630,7 @@ class TreemapController extends chart_js.DatasetController {
572
630
  if (!rect.hidden) {
573
631
  rect.draw(ctx);
574
632
  const opts = rect.options;
575
- if (shouldDrawCaption(rect, opts.font) && item.g) {
633
+ if (shouldDrawCaption(rect, opts.captions.font)) {
576
634
  drawCaption(ctx, rect, item, opts, levels);
577
635
  }
578
636
  }
@@ -599,12 +657,25 @@ TreemapController.version = version;
599
657
  TreemapController.defaults = {
600
658
  dataElementType: 'treemap',
601
659
 
602
- groupLabels: true,
660
+ animations: {
661
+ numbers: {
662
+ type: 'number',
663
+ properties: ['x', 'y', 'width', 'height']
664
+ },
665
+ },
666
+
603
667
  borderWidth: 0,
604
668
  spacing: 0.5,
605
- groupDividers: false,
606
- dividerWidth: 1
669
+ dividers: {
670
+ display: false,
671
+ lineWidth: 1,
672
+ }
673
+
674
+ };
607
675
 
676
+ TreemapController.descriptors = {
677
+ _scriptable: true,
678
+ _indexable: false
608
679
  };
609
680
 
610
681
  TreemapController.overrides = {
@@ -648,6 +719,10 @@ TreemapController.overrides = {
648
719
  },
649
720
  };
650
721
 
722
+ TreemapController.beforeRegister = function() {
723
+ requireVersion('3.6', chart_js.Chart.version);
724
+ };
725
+
651
726
  TreemapController.afterRegister = function() {
652
727
  const tooltipPlugin = chart_js.registry.plugins.get('tooltip');
653
728
  if (tooltipPlugin) {
@@ -772,7 +847,6 @@ class TreemapElement extends chart_js.Element {
772
847
  ctx.fillStyle = options.backgroundColor;
773
848
  ctx.fillRect(inner.x, inner.y, inner.w, inner.h);
774
849
  }
775
-
776
850
  ctx.restore();
777
851
  }
778
852
 
@@ -808,20 +882,40 @@ class TreemapElement extends chart_js.Element {
808
882
  TreemapElement.id = 'treemap';
809
883
 
810
884
  TreemapElement.defaults = {
811
- borderSkipped: undefined,
812
885
  borderWidth: undefined,
813
- color: undefined,
814
- dividerCapStyle: 'butt',
815
- dividerColor: 'black',
816
- dividerDash: undefined,
817
- dividerDashOffset: 0,
818
- dividerWidth: 0,
819
- font: {},
820
- groupDividers: false,
821
- groupLabels: undefined,
822
886
  spacing: undefined,
823
887
  label: undefined,
824
- rtl: undefined
888
+ rtl: undefined,
889
+ dividers: {
890
+ display: false,
891
+ lineCapStyle: 'butt',
892
+ lineColor: 'black',
893
+ lineDash: undefined,
894
+ lineDashOffset: 0,
895
+ lineWidth: 0,
896
+ },
897
+ captions: {
898
+ align: undefined,
899
+ color: undefined,
900
+ display: true,
901
+ formatter: (ctx) => ctx.raw.g || '',
902
+ font: {},
903
+ padding: 3
904
+ },
905
+ labels: {
906
+ align: 'center',
907
+ color: undefined,
908
+ display: false,
909
+ formatter: (ctx) => ctx.raw.g ? [ctx.raw.g, ctx.raw.v] : ctx.raw.v,
910
+ font: {},
911
+ position: 'middle',
912
+ padding: 3
913
+ }
914
+ };
915
+
916
+ TreemapElement.descriptors = {
917
+ _scriptable: true,
918
+ _indexable: false
825
919
  };
826
920
 
827
921
  TreemapElement.defaultRoutes = {
@@ -835,9 +929,10 @@ exports.flatten = flatten;
835
929
  exports.group = group;
836
930
  exports.index = index;
837
931
  exports.isObject = isObject;
932
+ exports.requireVersion = requireVersion;
838
933
  exports.sort = sort;
839
934
  exports.sum = sum;
840
935
 
841
936
  Object.defineProperty(exports, '__esModule', { value: true });
842
937
 
843
- })));
938
+ }));
@@ -1,7 +1,7 @@
1
1
  /*!
2
- * chartjs-chart-treemap v1.0.2
2
+ * chartjs-chart-treemap v2.0.2
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,i){"use strict";function n(t){const e=[...t],i=[];for(;e.length;){const t=e.pop();Array.isArray(t)?e.push(...t):i.push(t)}return i.reverse()}function r(t,e,i,n,r){const o=Object.create(null),s=Object.create(null),a=[];let l,h,u,d;for(h=0,u=t.length;h<u;++h)d=t[h],n&&d[n]!==r||(l=d[e]||"",l in o||(o[l]=0,s[l]=[]),o[l]+=+d[i],s[l].push(d));return Object.keys(o).forEach((t=>{d={children:s[t]},d[i]=+o[t],d[e]=t,n&&(d[n]=r),a.push(d)})),a}function o(t){const e=typeof t;return"function"===e||"object"===e&&!!t}function s(t,e){let i,n=t.length;if(!n)return e;const r=o(t[0]);for(e=r?e:"v",i=0,n=t.length;i<n;++i)r?t[i]._idx=i:t[i]={v:t[i],_idx:i};return e}function a(t,e){e?t.sort(((t,i)=>+i[e]-+t[e])):t.sort(((t,e)=>+e-+t))}function l(t,e){let i,n,r;for(i=0,n=0,r=t.length;n<r;++n)i+=e?+t[n][e]:+t[n];return i}function h(t,e){return+(Math.round(t+"e+"+e)+"e-"+e)||0}function u(t,e,i,n){const r=t._normalized,o=e*r/i,s=Math.sqrt(r*o),a=r/s;return{d1:s,d2:a,w:"_ix"===n?s:a,h:"_ix"===n?a:s}}const d=(t,e)=>h(t.rtl?t.x+t.w-t._ix-e:t.x+t._ix,4);function c(t,e,i,n){const r={x:d(t,i.w),y:h(t.y+t._iy,4),w:h(i.w,4),h:h(i.h,4),a:h(e._normalized,4),v:e.value,s:n,_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,i=[],n=t.nsum,r=t.get(),o=e.dir,s=e.side,a=s*s,l="x"===o?"_ix":"_iy",h=n*n;let d=0,g=0;for(const n of r){const r=u(n,a,h,l);g+=r.d1,d=Math.max(d,r.d2),i.push(c(e,n,r,t.sum)),e[l]+=r.d1}return e["y"===o?"_ix":"_iy"]+=d,e[l]-=g,i}}const p=Math.min,f=Math.max;function m(t,e){const i=+e[t.key],n=i*t.ratio;return e._normalized=n,{min:p(t.min,i),max:f(t.max,i),sum:t.sum+i,nmin:p(t.nmin,n),nmax:f(t.nmax,n),nsum:t.nsum+n}}function x(t,e,i){t._arr.push(e),function(t,e){Object.assign(t,e)}(t,i)}class y{constructor(t,e){const i=this;i.key=t,i.ratio=e,i.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,...i){const n=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}),n,i))return t;var r;x(this,t,n)}get(){return this._arr}}function v(t,e,i){if(0===t.sum)return!0;const[n]=i,r=t.nsum*t.nsum,o=e.nsum*e.nsum,s=n*n,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,i,r,o,h){t=t||[];const u=[],d=new g(e),c=new y("value",d.area/l(t,i));let p=d.side;const f=t.length;let m,x;if(!f)return u;const b=t.slice();i=s(b,i),a(b,i);const w=t=>r&&b[t][r];for(m=0;m<f;++m)x={value:(_=m,i?+b[_][i]:+b[_]),groupSum:h,_data:t[b[m]._idx],level:void 0,group:void 0},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)),n(u)}function w(t,e){if(!e)return!1;const i=t.width||t.w,n=t.height||t.h,r=2*e.lineHeight;return i>r&&n>r}function _(t,e,i,n,r){if(t.save(),t.fillStyle=n.color,t.font=n.font.string,t.beginPath(),t.rect(e.x,e.y,e.width,e.height),t.clip(),"l"in i&&i.l!==r){if(n.groupLabels){t.textAlign=n.rtl?"end":"start",t.textBaseline="top";const r=n.rtl?e.x+e.width-n.borderWidth-3:e.x+n.borderWidth+3;t.fillText(i.g,r,e.y+n.borderWidth+3)}}else t.textAlign="center",t.textBaseline="middle",function(t,e,i){const n=i.options,r=n.font.lineHeight,o=(n.label||e.g+"\n"+e.v).split("\n"),s=i.y+i.height/2-o.length*r/4;o.forEach(((e,n)=>t.fillText(e,i.x+i.width/2,s+n*r)))}(t,i,e);t.restore()}function D(t,e){const i=e.options,n=e.width||e.w,r=e.height||e.h;if(t.save(),t.strokeStyle=i.dividerColor||"black",t.lineCap=i.dividerCapStyle,t.setLineDash(i.dividerDash||[]),t.lineDashOffset=i.dividerDashOffset,t.lineWidth=i.dividerWidth,t.beginPath(),n>r){const i=n/2;t.moveTo(e.x+i,e.y),t.lineTo(e.x+i,e.y+r)}else{const i=r/2;t.moveTo(e.x,e.y+i),t.lineTo(e.x+n,e.y+i)}t.stroke(),t.restore()}class O 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,n=e.getMeta(),o=e.getDataset(),s=o.groups||(o.groups=[]),a=i.toFont(o.font),l=e.chart.chartArea,h=o.key||"",u=!!o.rtl,d={x:l.left,y:l.top,w:l.right-l.left,h:l.bottom-l.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===h&&!function(t,e){let i,n;if(t.lenght!==e.length)return!0;for(i=0,n=t.length;i<n;++i)if(t[i]!==e[i])return!0;return!1}(e._groups,s)||(e._rect=d,e._groups=s.slice(),e._key=h,o.data=function(t,e,n){const o=t.key||"";let s=t.tree||[];const a=t.groups||[],l=a.length,h=(t.spacing||0)+(t.borderWidth||0);return!s.length&&t.data.length&&(s=t.tree=t.data),l?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<l-1&&m.forEach((r=>{y={x:r.x+h,y:r.y+h,w:r.w-2*h,h:r.h-2*h},i.valueOrDefault(t.groupLabels,!0)&&w(r,n)&&(y.y+=n.lineHeight,y.h-=n.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(n.data,0,n.data.length,t)}resolveDataElementOptions(t,e){const n=super.resolveDataElementOptions(t,e),r=Object.isFrozen(n)?Object.assign({},n):n;return r.font=i.toFont(n.font),r}updateElements(t,e,i,n){const r=this,o="reset"===n,s=r.getDataset(),a=r._rect.options=r.resolveDataElementOptions(e,n),l=r.getSharedOptions(a),h=r.includeOptions(n,l);for(let a=e;a<e+i;a++){const e=s.data[a],i=l||r.resolveDataElementOptions(a,n),u=o?0:e.h-2*i.spacing,d=o?0:e.w-2*i.spacing,c={x:e.x+i.spacing,y:e.y+i.spacing,width:d,height:u};h&&(c.options=i),r.updateElement(t[a],a,c,n)}r.updateSharedOptions(l,n,a)}_drawDividers(t,e,i){for(let n=0,r=i.length;n<r;++n){const r=i[n],o=e[n];r.options.groupDividers&&o._data.children.length>1&&D(t,r)}this.getDataset().groupDividers&&D(t,this._rect)}_drawRects(t,e,i,n){for(let r=0,o=i.length;r<o;++r){const o=i[r],s=e[r];if(!o.hidden){o.draw(t);const e=o.options;w(o,e.font)&&s.g&&_(t,o,s,e,n)}}}draw(){const t=this,e=t.chart.ctx,i=t.getMeta().data||[],n=t.getDataset(),r=(n.groups||[]).length-1,o=n.data||[];t._drawRects(e,o,i,r),t._drawDividers(e,o,i)}}function k(t,e){const{x:i,y:n,width:r,height:o}=t.getProps(["x","y","width","height"],e);return{left:i,top:n,right:i+r,bottom:n+o}}function C(t,e,i){return Math.max(Math.min(t,i),e)}function j(t){const e=k(t),i=e.right-e.left,n=e.bottom-e.top,r=function(t,e,i){let n,r,s,a;return o(t)?(n=+t.top||0,r=+t.right||0,s=+t.bottom||0,a=+t.left||0):n=r=s=a=+t||0,{t:C(n,0,i),r:C(r,0,e),b:C(s,0,i),l:C(a,0,e)}}(t.options.borderWidth,i/2,n/2);return{outer:{x:e.left,y:e.top,w:i,h:n},inner:{x:e.left+r.l,y:e.top+r.t,w:i-r.l-r.r,h:n-r.t-r.b}}}function E(t,e,i,n){const r=null===e,o=null===i,s=!(!t||r&&o)&&k(t,n);return s&&(r||e>=s.left&&e<=s.right)&&(o||i>=s.top&&i<=s.bottom)}O.id="treemap",O.version="1.0.2",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,i=e.data[t.dataIndex],n=i.g||e.label;return(n?n+": ":"")+i.v}}}},scales:{x:{type:"linear",display:!1},y:{type:"linear",display:!1}}},O.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()})},O.afterUnregister=function(){const t=e.registry.plugins.get("tooltip");t&&delete t.positioners.treemap};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:i,outer:n}=j(this);t.save(),n.w!==i.w||n.h!==i.h?(t.beginPath(),t.rect(n.x,n.y,n.w,n.h),t.clip(),t.rect(i.x,i.y,i.w,i.h),t.fillStyle=e.backgroundColor,t.fill(),t.fillStyle=e.borderColor,t.fill("evenodd")):(t.fillStyle=e.backgroundColor,t.fillRect(i.x,i.y,i.w,i.h)),t.restore()}inRange(t,e,i){return E(this,t,e,i)}inXRange(t,e){return E(this,t,null,e)}inYRange(t,e){return E(this,null,t,e)}getCenterPoint(t){const{x:e,y:i,width:n,height:r}=this.getProps(["x","y","width","height"],t);return{x:e+n/2,y:i+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),t.flatten=n,t.group=r,t.index=s,t.isObject=o,t.sort=a,t.sum=l,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=n.spacing,c=2*u,d={x:e.x+u,y:e.y+u,width:o?0:e.w-c,height:o?0:e.h-c,hidden:c>e.w||c>e.h};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="2.0.2",k.defaults={dataElementType:"treemap",animations:{numbers:{type:"number",properties:["x","y","width","height"]}},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,7 +1,7 @@
1
1
  {
2
2
  "name": "chartjs-chart-treemap",
3
3
  "homepage": "https://chartjs-chart-treemap.pages.dev/",
4
- "version": "1.0.2",
4
+ "version": "2.0.2",
5
5
  "description": "Chart.js module for creating treemap charts",
6
6
  "main": "dist/chartjs-chart-treemap.js",
7
7
  "module": "dist/chartjs-chart-treemap.esm.js",
@@ -39,18 +39,18 @@
39
39
  "url": "https://github.com/kurkle/chartjs-chart-treemap/issues"
40
40
  },
41
41
  "devDependencies": {
42
- "@rollup/plugin-commonjs": "^19.0.0",
42
+ "@rollup/plugin-commonjs": "^21.0.1",
43
43
  "@rollup/plugin-json": "^4.1.0",
44
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.1.0",
45
+ "@typescript-eslint/eslint-plugin": "^5.4.0",
46
+ "@typescript-eslint/parser": "^5.4.0",
47
+ "chart.js": "^3.6.0",
48
48
  "chartjs-adapter-date-fns": "^2.0.0",
49
49
  "chartjs-test-utils": "^0.3.0",
50
50
  "concurrently": "^6.0.1",
51
51
  "cross-env": "^7.0.3",
52
52
  "date-fns": "^2.20.2",
53
- "eslint": "^7.24.0",
53
+ "eslint": "^8.3.0",
54
54
  "eslint-config-chartjs": "^0.3.0",
55
55
  "eslint-plugin-es": "^4.1.0",
56
56
  "eslint-plugin-html": "^6.1.2",
@@ -64,15 +64,15 @@
64
64
  "karma-jasmine-html-reporter": "^1.5.4",
65
65
  "karma-rollup-preprocessor": "^7.0.7",
66
66
  "karma-spec-reporter": "0.0.32",
67
- "karma-summary-reporter": "^2.0.2",
67
+ "karma-summary-reporter": "^3.0.0",
68
68
  "pixelmatch": "^5.2.1",
69
69
  "rollup": "^2.45.1",
70
70
  "rollup-plugin-analyzer": "^4.0.0",
71
71
  "rollup-plugin-istanbul": "^3.0.0",
72
72
  "rollup-plugin-terser": "^7.0.2",
73
- "typescript": "^4.2.4",
73
+ "typescript": "^4.3.5",
74
74
  "vuepress": "^1.8.2",
75
- "vuepress-plugin-flexsearch": "^0.2.0",
75
+ "vuepress-plugin-flexsearch": "^0.3.0",
76
76
  "vuepress-plugin-redirect": "^1.2.5",
77
77
  "vuepress-theme-chartjs": "^0.2.0"
78
78
  },
@@ -6,31 +6,62 @@ import {
6
6
  ScriptableContext, Color, Scriptable, FontSpec
7
7
  } from 'chart.js';
8
8
 
9
+ type TreemapScriptableContext = ScriptableContext<'treemap'> & {
10
+ raw: TreemapDataPoint
11
+ }
9
12
 
10
- export interface TreemapControllerDatasetOptions<DType> {
11
- color?: Scriptable<Color, ScriptableContext<'treemap'>>,
12
- dividerCapStyle?: string,
13
- dividerColor?: string,
14
- dividerDash?: number[],
15
- dividerDashOffset?: number,
16
- dividerWidth?: number,
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>,
17
29
  font?: FontSpec,
18
- groupDividers?: boolean,
19
- groupLabels?: boolean,
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> {
20
50
  spacing?: number,
21
51
  rtl?: boolean,
22
52
 
23
- backgroundColor?: Scriptable<Color, ScriptableContext<'treemap'>>;
24
- borderColor?: Scriptable<Color, ScriptableContext<'treemap'>>;
53
+ backgroundColor?: Scriptable<Color, TreemapScriptableContext>;
54
+ borderColor?: Scriptable<Color, TreemapScriptableContext>;
25
55
  borderWidth?: number;
26
56
 
27
- hoverColor?: Scriptable<Color, ScriptableContext<'treemap'>>,
28
- hoverFont?: FontSpec,
29
- hoverBackgroundColor?: Scriptable<Color, ScriptableContext<'treemap'>>;
30
- hoverBorderColor?: Scriptable<Color, ScriptableContext<'treemap'>>;
57
+ hoverBackgroundColor?: Scriptable<Color, TreemapScriptableContext>;
58
+ hoverBorderColor?: Scriptable<Color, TreemapScriptableContext>;
31
59
  hoverBorderWidth?: number;
32
60
 
33
- label?: Scriptable<string, ScriptableContext<'treemap'>>;
61
+ captions?: TreemapControllerDatasetCaptionsOptions;
62
+ dividers?: TreemapControllerDatasetDividersOptions;
63
+ labels?: TreemapControllerDatasetLabelsOptions;
64
+ label?: string;
34
65
 
35
66
  data: TreemapDataPoint[]; // This will be auto-generated from `tree`
36
67
  groups?: Array<keyof DType>;
@@ -76,6 +107,7 @@ declare module 'chart.js' {
76
107
  chartOptions: CoreChartOptions<'treemap'>;
77
108
  datasetOptions: TreemapControllerDatasetOptions<Record<string, unknown>>;
78
109
  defaultDataPoint: TreemapDataPoint;
110
+ metaExtensions: {};
79
111
  parsedDataType: unknown,
80
112
  scales: never;
81
113
  }