chartjs-chart-treemap 1.0.2 → 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/README.md CHANGED
@@ -1,6 +1,6 @@
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)
@@ -1,11 +1,11 @@
1
1
  /*!
2
- * chartjs-chart-treemap v1.0.2
2
+ * chartjs-chart-treemap v1.0.3
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 = "1.0.3";
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
 
@@ -553,13 +614,11 @@ class TreemapController extends DatasetController {
553
614
  for (let i = 0, ilen = metadata.length; i < ilen; ++i) {
554
615
  const rect = metadata[i];
555
616
  const item = data[i];
556
- if (rect.options.groupDividers && item._data.children.length > 1) {
617
+ const dividersOpts = rect.options.dividers || {};
618
+ if (dividersOpts.display && item._data.children.length > 1) {
557
619
  drawDivider(ctx, rect);
558
620
  }
559
621
  }
560
- if (this.getDataset().groupDividers) {
561
- drawDivider(ctx, this._rect);
562
- }
563
622
  }
564
623
 
565
624
  _drawRects(ctx, data, metadata, levels) {
@@ -569,7 +628,7 @@ class TreemapController extends DatasetController {
569
628
  if (!rect.hidden) {
570
629
  rect.draw(ctx);
571
630
  const opts = rect.options;
572
- if (shouldDrawCaption(rect, opts.font) && item.g) {
631
+ if (shouldDrawCaption(rect, opts.captions.font)) {
573
632
  drawCaption(ctx, rect, item, opts, levels);
574
633
  }
575
634
  }
@@ -596,14 +655,20 @@ TreemapController.version = version;
596
655
  TreemapController.defaults = {
597
656
  dataElementType: 'treemap',
598
657
 
599
- groupLabels: true,
600
658
  borderWidth: 0,
601
659
  spacing: 0.5,
602
- groupDividers: false,
603
- dividerWidth: 1
660
+ dividers: {
661
+ display: false,
662
+ lineWidth: 1,
663
+ }
604
664
 
605
665
  };
606
666
 
667
+ TreemapController.descriptors = {
668
+ _scriptable: true,
669
+ _indexable: false
670
+ };
671
+
607
672
  TreemapController.overrides = {
608
673
  interaction: {
609
674
  mode: 'point',
@@ -645,6 +710,10 @@ TreemapController.overrides = {
645
710
  },
646
711
  };
647
712
 
713
+ TreemapController.beforeRegister = function() {
714
+ requireVersion('3.6', Chart.version);
715
+ };
716
+
648
717
  TreemapController.afterRegister = function() {
649
718
  const tooltipPlugin = registry.plugins.get('tooltip');
650
719
  if (tooltipPlugin) {
@@ -769,7 +838,6 @@ class TreemapElement extends Element {
769
838
  ctx.fillStyle = options.backgroundColor;
770
839
  ctx.fillRect(inner.x, inner.y, inner.w, inner.h);
771
840
  }
772
-
773
841
  ctx.restore();
774
842
  }
775
843
 
@@ -805,20 +873,40 @@ class TreemapElement extends Element {
805
873
  TreemapElement.id = 'treemap';
806
874
 
807
875
  TreemapElement.defaults = {
808
- borderSkipped: undefined,
809
876
  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
877
  spacing: undefined,
820
878
  label: undefined,
821
- 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
822
910
  };
823
911
 
824
912
  TreemapElement.defaultRoutes = {
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * chartjs-chart-treemap v1.0.2
2
+ * chartjs-chart-treemap v1.0.3
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 = "1.0.3";
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
 
@@ -556,13 +617,11 @@ class TreemapController extends chart_js.DatasetController {
556
617
  for (let i = 0, ilen = metadata.length; i < ilen; ++i) {
557
618
  const rect = metadata[i];
558
619
  const item = data[i];
559
- if (rect.options.groupDividers && item._data.children.length > 1) {
620
+ const dividersOpts = rect.options.dividers || {};
621
+ if (dividersOpts.display && item._data.children.length > 1) {
560
622
  drawDivider(ctx, rect);
561
623
  }
562
624
  }
563
- if (this.getDataset().groupDividers) {
564
- drawDivider(ctx, this._rect);
565
- }
566
625
  }
567
626
 
568
627
  _drawRects(ctx, data, metadata, levels) {
@@ -572,7 +631,7 @@ class TreemapController extends chart_js.DatasetController {
572
631
  if (!rect.hidden) {
573
632
  rect.draw(ctx);
574
633
  const opts = rect.options;
575
- if (shouldDrawCaption(rect, opts.font) && item.g) {
634
+ if (shouldDrawCaption(rect, opts.captions.font)) {
576
635
  drawCaption(ctx, rect, item, opts, levels);
577
636
  }
578
637
  }
@@ -599,14 +658,20 @@ TreemapController.version = version;
599
658
  TreemapController.defaults = {
600
659
  dataElementType: 'treemap',
601
660
 
602
- groupLabels: true,
603
661
  borderWidth: 0,
604
662
  spacing: 0.5,
605
- groupDividers: false,
606
- dividerWidth: 1
663
+ dividers: {
664
+ display: false,
665
+ lineWidth: 1,
666
+ }
607
667
 
608
668
  };
609
669
 
670
+ TreemapController.descriptors = {
671
+ _scriptable: true,
672
+ _indexable: false
673
+ };
674
+
610
675
  TreemapController.overrides = {
611
676
  interaction: {
612
677
  mode: 'point',
@@ -648,6 +713,10 @@ TreemapController.overrides = {
648
713
  },
649
714
  };
650
715
 
716
+ TreemapController.beforeRegister = function() {
717
+ requireVersion('3.6', chart_js.Chart.version);
718
+ };
719
+
651
720
  TreemapController.afterRegister = function() {
652
721
  const tooltipPlugin = chart_js.registry.plugins.get('tooltip');
653
722
  if (tooltipPlugin) {
@@ -772,7 +841,6 @@ class TreemapElement extends chart_js.Element {
772
841
  ctx.fillStyle = options.backgroundColor;
773
842
  ctx.fillRect(inner.x, inner.y, inner.w, inner.h);
774
843
  }
775
-
776
844
  ctx.restore();
777
845
  }
778
846
 
@@ -808,20 +876,40 @@ class TreemapElement extends chart_js.Element {
808
876
  TreemapElement.id = 'treemap';
809
877
 
810
878
  TreemapElement.defaults = {
811
- borderSkipped: undefined,
812
879
  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
880
  spacing: undefined,
823
881
  label: undefined,
824
- 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
825
913
  };
826
914
 
827
915
  TreemapElement.defaultRoutes = {
@@ -835,9 +923,10 @@ exports.flatten = flatten;
835
923
  exports.group = group;
836
924
  exports.index = index;
837
925
  exports.isObject = isObject;
926
+ exports.requireVersion = requireVersion;
838
927
  exports.sort = sort;
839
928
  exports.sum = sum;
840
929
 
841
930
  Object.defineProperty(exports, '__esModule', { value: true });
842
931
 
843
- })));
932
+ }));
@@ -1,7 +1,7 @@
1
1
  /*!
2
- * chartjs-chart-treemap v1.0.2
2
+ * chartjs-chart-treemap v1.0.3
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=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,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": "1.0.3",
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",
@@ -44,7 +44,7 @@
44
44
  "@rollup/plugin-node-resolve": "^13.0.0",
45
45
  "@typescript-eslint/eslint-plugin": "^4.22.0",
46
46
  "@typescript-eslint/parser": "^4.22.0",
47
- "chart.js": "^3.1.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",
@@ -70,7 +70,7 @@
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
75
  "vuepress-plugin-flexsearch": "^0.2.0",
76
76
  "vuepress-plugin-redirect": "^1.2.5",
@@ -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
  }