chartjs-chart-treemap 2.0.2 → 2.1.1

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.
@@ -1,7 +1,7 @@
1
1
  /*!
2
- * chartjs-chart-treemap v2.0.2
2
+ * chartjs-chart-treemap v2.1.1
3
3
  * https://chartjs-chart-treemap.pages.dev/
4
- * (c) 2021 Jukka Kurkela
4
+ * (c) 2022 Jukka Kurkela
5
5
  * Released under the MIT license
6
6
  */
7
7
  (function (global, factory) {
@@ -10,6 +10,65 @@ typeof define === 'function' && define.amd ? define(['exports', 'chart.js', 'cha
10
10
  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["chartjs-chart-treemap"] = {}, global.Chart, global.Chart.helpers));
11
11
  })(this, (function (exports, chart_js, helpers) { 'use strict';
12
12
 
13
+ const getValue = (item) => helpers.isObject(item) ? item.v : item;
14
+
15
+ const maxValue = (data) => data.reduce(function(m, v) {
16
+ return (m > getValue(v) ? m : getValue(v));
17
+ }, 0);
18
+
19
+ const minValue = (data, mx) => data.reduce(function(m, v) {
20
+ return (m < getValue(v) ? m : getValue(v));
21
+ }, mx);
22
+
23
+ const getGroupKey = (lvl) => '' + lvl;
24
+
25
+ function scanTreeObject(key, treeLeafKey, obj, tree = [], lvl = 0, result = []) {
26
+ const objIndex = lvl - 1;
27
+ if (key in obj && lvl > 0) {
28
+ const record = tree.reduce(function(reduced, item, i) {
29
+ if (i !== objIndex) {
30
+ reduced[getGroupKey(i)] = item;
31
+ }
32
+ return reduced;
33
+ }, {});
34
+ record[treeLeafKey] = tree[objIndex];
35
+ record[key] = obj[key];
36
+ result.push(record);
37
+ } else {
38
+ for (const childKey of Object.keys(obj)) {
39
+ const child = obj[childKey];
40
+ if (helpers.isObject(child)) {
41
+ tree.push(childKey);
42
+ scanTreeObject(key, treeLeafKey, child, tree, lvl + 1, result);
43
+ }
44
+ }
45
+ }
46
+ tree.splice(objIndex, 1);
47
+ return result;
48
+ }
49
+
50
+ function normalizeTreeToArray(key, treeLeafKey, obj) {
51
+ const data = scanTreeObject(key, treeLeafKey, obj);
52
+ if (!data.length) {
53
+ return data;
54
+ }
55
+ const max = data.reduce(function(maxVal, element) {
56
+ // minus 2 because _leaf and value properties are added
57
+ // on top to groups ones
58
+ const keys = Object.keys(element).length - 2;
59
+ return maxVal > keys ? maxVal : keys;
60
+ });
61
+ data.forEach(function(element) {
62
+ for (let i = 0; i < max; i++) {
63
+ const groupKey = getGroupKey(i);
64
+ if (!element[groupKey]) {
65
+ element[groupKey] = '';
66
+ }
67
+ }
68
+ });
69
+ return data;
70
+ }
71
+
13
72
  // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat
14
73
  function flatten(input) {
15
74
  const stack = [...input];
@@ -28,36 +87,59 @@ function flatten(input) {
28
87
  return res.reverse();
29
88
  }
30
89
 
90
+ function getPath(groups, value, defaultValue) {
91
+ if (!groups.length) {
92
+ return;
93
+ }
94
+ const path = [];
95
+ for (const grp of groups) {
96
+ const item = value[grp];
97
+ if (item === '') {
98
+ path.push(defaultValue);
99
+ break;
100
+ }
101
+ path.push(item);
102
+ }
103
+ return path.length ? path.join('.') : defaultValue;
104
+ }
105
+
31
106
  /**
32
107
  * @param {[]} values
33
108
  * @param {string} grp
34
109
  * @param {string} key
110
+ * @param {string} treeeLeafKey
35
111
  * @param {string} [mainGrp]
36
112
  * @param {*} [mainValue]
113
+ * @param {[]} groups
37
114
  */
38
- function group(values, grp, key, mainGrp, mainValue) {
115
+ function group(values, grp, key, treeLeafKey, mainGrp, mainValue, groups = []) {
39
116
  const tmp = Object.create(null);
40
117
  const data = Object.create(null);
41
118
  const ret = [];
42
- let g, i, n, v;
119
+ let g, i, n;
43
120
  for (i = 0, n = values.length; i < n; ++i) {
44
- v = values[i];
121
+ const v = values[i];
45
122
  if (mainGrp && v[mainGrp] !== mainValue) {
46
123
  continue;
47
124
  }
48
- g = v[grp] || '';
125
+ g = v[grp] || v[treeLeafKey] || '';
49
126
  if (!(g in tmp)) {
50
- tmp[g] = 0;
127
+ tmp[g] = {value: 0};
51
128
  data[g] = [];
52
129
  }
53
- tmp[g] += +v[key];
130
+ tmp[g].value += +v[key];
131
+ tmp[g].label = v[grp] || '';
132
+ tmp[g].path = getPath(groups, v, g);
54
133
  data[g].push(v);
55
134
  }
56
135
 
57
136
  Object.keys(tmp).forEach((k) => {
58
- v = {children: data[k]};
59
- v[key] = +tmp[k];
60
- v[grp] = k;
137
+ const v = {children: data[k]};
138
+ v[key] = +tmp[k].value;
139
+ v[grp] = tmp[k].label;
140
+ v.label = k;
141
+ v.path = tmp[k].path;
142
+
61
143
  if (mainGrp) {
62
144
  v[mainGrp] = mainValue;
63
145
  }
@@ -67,11 +149,6 @@ function group(values, grp, key, mainGrp, mainValue) {
67
149
  return ret;
68
150
  }
69
151
 
70
- function isObject(obj) {
71
- const type = typeof obj;
72
- return type === 'function' || type === 'object' && !!obj;
73
- }
74
-
75
152
  function index(values, key) {
76
153
  let n = values.length;
77
154
  let i;
@@ -80,7 +157,7 @@ function index(values, key) {
80
157
  return key;
81
158
  }
82
159
 
83
- const obj = isObject(values[0]);
160
+ const obj = helpers.isObject(values[0]);
84
161
  key = obj ? key : 'v';
85
162
 
86
163
  for (i = 0, n = values.length; i < n; ++i) {
@@ -118,35 +195,427 @@ function requireVersion(min, ver) {
118
195
  }
119
196
  }
120
197
 
121
- function round(v, n) {
122
- // @ts-ignore
123
- return (+(Math.round(v + 'e+' + n) + 'e-' + n)) || 0;
198
+ const rasterize = (v, dpr) => Math.round(v * dpr) / dpr;
199
+
200
+ function rasterizeRect(rect, dpr) {
201
+ return {
202
+ ...rect,
203
+ x: rasterize(rect.x, dpr),
204
+ y: rasterize(rect.y, dpr),
205
+ w: rasterize(rect.w, dpr),
206
+ h: rasterize(rect.h, dpr),
207
+ };
208
+ }
209
+
210
+ const widthCache = new Map();
211
+
212
+ /**
213
+ * Helper function to get the bounds of the rect
214
+ * @param {TreemapElement} rect the rect
215
+ * @param {boolean} [useFinalPosition]
216
+ * @return {object} bounds of the rect
217
+ * @private
218
+ */
219
+ function getBounds(rect, useFinalPosition) {
220
+ const {x, y, width, height} = rect.getProps(['x', 'y', 'width', 'height'], useFinalPosition);
221
+ return {left: x, top: y, right: x + width, bottom: y + height};
222
+ }
223
+
224
+ function limit(value, min, max) {
225
+ return Math.max(Math.min(value, max), min);
226
+ }
227
+
228
+ function parseBorderWidth(value, maxW, maxH) {
229
+ const o = helpers.toTRBL(value);
230
+
231
+ return {
232
+ t: limit(o.top, 0, maxH),
233
+ r: limit(o.right, 0, maxW),
234
+ b: limit(o.bottom, 0, maxH),
235
+ l: limit(o.left, 0, maxW)
236
+ };
124
237
  }
125
238
 
126
- function getDims(itm, w2, s2, key) {
239
+ function parseBorderRadius(value, maxW, maxH) {
240
+ const o = helpers.toTRBLCorners(value);
241
+ const maxR = Math.min(maxW, maxH);
242
+
243
+ return {
244
+ topLeft: limit(o.topLeft, 0, maxR),
245
+ topRight: limit(o.topRight, 0, maxR),
246
+ bottomLeft: limit(o.bottomLeft, 0, maxR),
247
+ bottomRight: limit(o.bottomRight, 0, maxR)
248
+ };
249
+ }
250
+
251
+ function boundingRects(rect, dpr) {
252
+ const bounds = getBounds(rect);
253
+ const width = bounds.right - bounds.left;
254
+ const height = bounds.bottom - bounds.top;
255
+ const border = parseBorderWidth(rect.options.borderWidth, width / 2, height / 2);
256
+ const radius = parseBorderRadius(rect.options.borderRadius, width / 2, height / 2);
257
+ const outer = rasterizeRect({
258
+ x: bounds.left,
259
+ y: bounds.top,
260
+ w: width,
261
+ h: height,
262
+ radius
263
+ }, dpr);
264
+
265
+ return {
266
+ outer,
267
+ inner: {
268
+ x: outer.x + border.l,
269
+ y: outer.y + border.t,
270
+ w: outer.w - border.l - border.r,
271
+ h: outer.h - border.t - border.b,
272
+ radius: {
273
+ topLeft: Math.max(0, radius.topLeft - Math.max(border.t, border.l)),
274
+ topRight: Math.max(0, radius.topRight - Math.max(border.t, border.r)),
275
+ bottomLeft: Math.max(0, radius.bottomLeft - Math.max(border.b, border.l)),
276
+ bottomRight: Math.max(0, radius.bottomRight - Math.max(border.b, border.r)),
277
+ }
278
+ }
279
+ };
280
+ }
281
+
282
+ function inRange(rect, x, y, useFinalPosition) {
283
+ const skipX = x === null;
284
+ const skipY = y === null;
285
+ const bounds = !rect || (skipX && skipY) ? false : getBounds(rect, useFinalPosition);
286
+
287
+ return bounds
288
+ && (skipX || x >= bounds.left && x <= bounds.right)
289
+ && (skipY || y >= bounds.top && y <= bounds.bottom);
290
+ }
291
+
292
+ function hasRadius(radius) {
293
+ return radius.topLeft || radius.topRight || radius.bottomLeft || radius.bottomRight;
294
+ }
295
+
296
+ /**
297
+ * Add a path of a rectangle to the current sub-path
298
+ * @param {CanvasRenderingContext2D} ctx Context
299
+ * @param {*} rect Bounding rect
300
+ */
301
+ function addNormalRectPath(ctx, rect) {
302
+ ctx.rect(rect.x, rect.y, rect.w, rect.h);
303
+ }
304
+
305
+ function shouldDrawCaption(rect, options) {
306
+ if (!options || options.display === false) {
307
+ return false;
308
+ }
309
+ const {w, h} = rect;
310
+ const font = helpers.toFont(options.font);
311
+ const min = font.lineHeight;
312
+ const padding = limit(helpers.valueOrDefault(options.padding, 3) * 2, 0, Math.min(w, h));
313
+ return (w - padding) > min && (h - padding) > min;
314
+ }
315
+
316
+ function drawText(ctx, rect, options, item, levels) {
317
+ const {captions, labels} = options;
318
+ ctx.save();
319
+ ctx.beginPath();
320
+ ctx.rect(rect.x, rect.y, rect.w, rect.h);
321
+ ctx.clip();
322
+ const isLeaf = (!('l' in item) || item.l === levels);
323
+ if (isLeaf && labels.display) {
324
+ drawLabel(ctx, rect, options);
325
+ } else if (!isLeaf && shouldDrawCaption(rect, captions)) {
326
+ drawCaption(ctx, rect, options, item);
327
+ }
328
+ ctx.restore();
329
+ }
330
+
331
+ function drawCaption(ctx, rect, options, item) {
332
+ const {captions, spacing, rtl} = options;
333
+ const {color, hoverColor, font, hoverFont, padding, align, formatter} = captions;
334
+ const oColor = (rect.active ? hoverColor : color) || color;
335
+ const oAlign = align || (rtl ? 'right' : 'left');
336
+ const optFont = (rect.active ? hoverFont : font) || font;
337
+ const oFont = helpers.toFont(optFont);
338
+ const lh = oFont.lineHeight / 2;
339
+ const x = calculateX(rect, oAlign, padding);
340
+ ctx.fillStyle = oColor;
341
+ ctx.font = oFont.string;
342
+ ctx.textAlign = oAlign;
343
+ ctx.textBaseline = 'middle';
344
+ ctx.fillText(formatter || item.g, x, rect.y + padding + spacing + lh);
345
+ }
346
+
347
+ function measureLabelSize(ctx, lines, fonts) {
348
+ const fontsKey = fonts.reduce(function(prev, item) {
349
+ prev += item.string;
350
+ return prev;
351
+ }, '');
352
+ const mapKey = lines.join() + fontsKey + (ctx._measureText ? '-spriting' : '');
353
+ if (!widthCache.has(mapKey)) {
354
+ ctx.save();
355
+ const count = lines.length;
356
+ let width = 0;
357
+ let height = 0;
358
+ for (let i = 0; i < count; i++) {
359
+ const font = fonts[Math.min(i, fonts.length - 1)];
360
+ ctx.font = font.string;
361
+ const text = lines[i];
362
+ width = Math.max(width, ctx.measureText(text).width);
363
+ height += font.lineHeight;
364
+ }
365
+ ctx.restore();
366
+ widthCache.set(mapKey, {width, height});
367
+ }
368
+ return widthCache.get(mapKey);
369
+ }
370
+
371
+ function labelToDraw(ctx, rect, options, labelSize) {
372
+ const {overflow, padding} = options;
373
+ const {width, height} = labelSize;
374
+ if (overflow === 'hidden') {
375
+ return !((width + padding * 2) > rect.w || (height + padding * 2) > rect.h);
376
+ }
377
+ return true;
378
+ }
379
+
380
+ function drawLabel(ctx, rect, options) {
381
+ const labels = options.labels;
382
+ const content = labels.formatter;
383
+ if (!content) {
384
+ return;
385
+ }
386
+ const contents = helpers.isArray(content) ? content : [content];
387
+ const {font, hoverFont} = labels;
388
+ const optFont = (rect.active ? hoverFont : font) || font;
389
+ const fonts = helpers.isArray(optFont) ? optFont.map(f => helpers.toFont(f)) : [helpers.toFont(optFont)];
390
+ const labelSize = measureLabelSize(ctx, contents, fonts);
391
+ if (!labelToDraw(ctx, rect, labels, labelSize)) {
392
+ return;
393
+ }
394
+ const {color, hoverColor, align} = labels;
395
+ const optColor = (rect.active ? hoverColor : color) || color;
396
+ const colors = helpers.isArray(optColor) ? optColor : [optColor];
397
+ const xyPoint = calculateXYLabel(rect, labels, labelSize);
398
+ ctx.textAlign = align;
399
+ ctx.textBaseline = 'middle';
400
+ let lhs = 0;
401
+ contents.forEach(function(l, i) {
402
+ const c = colors[Math.min(i, colors.length - 1)];
403
+ const f = fonts[Math.min(i, fonts.length - 1)];
404
+ const lh = f.lineHeight;
405
+ ctx.font = f.string;
406
+ ctx.fillStyle = c;
407
+ ctx.fillText(l, xyPoint.x, xyPoint.y + lh / 2 + lhs);
408
+ lhs += lh;
409
+ });
410
+ }
411
+
412
+ function drawDivider(ctx, rect, options, item) {
413
+ const dividers = options.dividers;
414
+ if (!dividers.display || !item._data.children.length) {
415
+ return;
416
+ }
417
+ const {x, y, w, h} = rect;
418
+ const {lineColor, lineCapStyle, lineDash, lineDashOffset, lineWidth} = dividers;
419
+ ctx.save();
420
+ ctx.strokeStyle = lineColor;
421
+ ctx.lineCap = lineCapStyle;
422
+ ctx.setLineDash(lineDash);
423
+ ctx.lineDashOffset = lineDashOffset;
424
+ ctx.lineWidth = lineWidth;
425
+ ctx.beginPath();
426
+ if (w > h) {
427
+ const w2 = w / 2;
428
+ ctx.moveTo(x + w2, y);
429
+ ctx.lineTo(x + w2, y + h);
430
+ } else {
431
+ const h2 = h / 2;
432
+ ctx.moveTo(x, y + h2);
433
+ ctx.lineTo(x + w, y + h2);
434
+ }
435
+ ctx.stroke();
436
+ ctx.restore();
437
+ }
438
+
439
+ function calculateXYLabel(rect, options, labelSize) {
440
+ const {align, position, padding} = options;
441
+ let x, y;
442
+ x = calculateX(rect, align, padding);
443
+ if (position === 'top') {
444
+ y = rect.y + padding;
445
+ } else if (position === 'bottom') {
446
+ y = rect.y + rect.h - padding - labelSize.height;
447
+ } else {
448
+ y = rect.y + (rect.h - labelSize.height) / 2 + padding;
449
+ }
450
+ return {x, y};
451
+ }
452
+
453
+ function calculateX(rect, align, padding) {
454
+ if (align === 'left') {
455
+ return rect.x + padding;
456
+ } else if (align === 'right') {
457
+ return rect.x + rect.w - padding;
458
+ }
459
+ return rect.x + rect.w / 2;
460
+ }
461
+
462
+ class TreemapElement extends chart_js.Element {
463
+
464
+ constructor(cfg) {
465
+ super();
466
+
467
+ this.options = undefined;
468
+ this.width = undefined;
469
+ this.height = undefined;
470
+
471
+ if (cfg) {
472
+ Object.assign(this, cfg);
473
+ }
474
+ }
475
+
476
+ draw(ctx, data, levels = 0, dpr) {
477
+ if (!data) {
478
+ return;
479
+ }
480
+ const options = this.options;
481
+ const {inner, outer} = boundingRects(this, dpr);
482
+
483
+ const addRectPath = hasRadius(outer.radius) ? helpers.addRoundedRectPath : addNormalRectPath;
484
+
485
+ ctx.save();
486
+
487
+ if (outer.w !== inner.w || outer.h !== inner.h) {
488
+ ctx.beginPath();
489
+ addRectPath(ctx, outer);
490
+ ctx.clip();
491
+ addRectPath(ctx, inner);
492
+ ctx.fillStyle = options.borderColor;
493
+ ctx.fill('evenodd');
494
+ }
495
+
496
+ ctx.beginPath();
497
+ addRectPath(ctx, inner);
498
+ ctx.fillStyle = options.backgroundColor;
499
+ ctx.fill();
500
+
501
+ drawDivider(ctx, inner, options, data);
502
+ drawText(ctx, inner, options, data, levels);
503
+ ctx.restore();
504
+ }
505
+
506
+ inRange(mouseX, mouseY, useFinalPosition) {
507
+ return inRange(this, mouseX, mouseY, useFinalPosition);
508
+ }
509
+
510
+ inXRange(mouseX, useFinalPosition) {
511
+ return inRange(this, mouseX, null, useFinalPosition);
512
+ }
513
+
514
+ inYRange(mouseY, useFinalPosition) {
515
+ return inRange(this, null, mouseY, useFinalPosition);
516
+ }
517
+
518
+ getCenterPoint(useFinalPosition) {
519
+ const {x, y, width, height} = this.getProps(['x', 'y', 'width', 'height'], useFinalPosition);
520
+ return {
521
+ x: x + width / 2,
522
+ y: y + height / 2
523
+ };
524
+ }
525
+
526
+ tooltipPosition() {
527
+ return this.getCenterPoint();
528
+ }
529
+
530
+ /**
531
+ * @todo: remove this unused function in v3
532
+ */
533
+ getRange(axis) {
534
+ return axis === 'x' ? this.width / 2 : this.height / 2;
535
+ }
536
+ }
537
+
538
+ TreemapElement.id = 'treemap';
539
+
540
+ TreemapElement.defaults = {
541
+ label: undefined,
542
+ borderRadius: 0,
543
+ borderWidth: 0,
544
+ captions: {
545
+ align: undefined,
546
+ color: 'black',
547
+ display: true,
548
+ font: {},
549
+ formatter: (ctx) => ctx.raw.g || ctx.raw._data.label || '',
550
+ padding: 3
551
+ },
552
+ dividers: {
553
+ display: false,
554
+ lineCapStyle: 'butt',
555
+ lineColor: 'black',
556
+ lineDash: [],
557
+ lineDashOffset: 0,
558
+ lineWidth: 1,
559
+ },
560
+ labels: {
561
+ align: 'center',
562
+ color: 'black',
563
+ display: false,
564
+ font: {},
565
+ formatter(ctx) {
566
+ if (ctx.raw.g) {
567
+ return [ctx.raw.g, ctx.raw.v + ''];
568
+ }
569
+ return ctx.raw._data.label ? [ctx.raw._data.label, ctx.raw.v + ''] : ctx.raw.v + '';
570
+ },
571
+ overflow: 'cut',
572
+ position: 'middle',
573
+ padding: 3
574
+ },
575
+ rtl: false,
576
+ spacing: 0.5
577
+ };
578
+
579
+ TreemapElement.descriptors = {
580
+ labels: {
581
+ _fallback: true
582
+ },
583
+ captions: {
584
+ _fallback: true
585
+ },
586
+ _scriptable: true,
587
+ _indexable: false
588
+ };
589
+
590
+ TreemapElement.defaultRoutes = {
591
+ backgroundColor: 'backgroundColor',
592
+ borderColor: 'borderColor'
593
+ };
594
+
595
+ function getDims(itm, w2, s2, key, dpr, maxd2) {
127
596
  const a = itm._normalized;
128
597
  const ar = w2 * a / s2;
129
- const d1 = Math.sqrt(a * ar);
130
- const d2 = a / d1;
598
+ const d1 = rasterize(Math.sqrt(a * ar), dpr);
599
+ const d2 = Math.min(rasterize(a / d1, dpr), maxd2);
131
600
  const w = key === '_ix' ? d1 : d2;
132
601
  const h = key === '_ix' ? d2 : d1;
133
602
 
134
603
  return {d1, d2, w, h};
135
604
  }
136
605
 
137
- const getX = (rect, w) => round(rect.rtl ? rect.x + rect.w - rect._ix - w : rect.x + rect._ix, 4);
606
+ const getX = (rect, w) => rect.rtl ? rect.x + rect.iw - w : rect.x + rect._ix;
138
607
 
139
- function buildRow(rect, itm, dims, sum) {
140
- const r = {
608
+ function buildRow(rect, itm, dims, sum, dpr) {
609
+ const r = rasterizeRect({
141
610
  x: getX(rect, dims.w),
142
- y: round(rect.y + rect._iy, 4),
143
- w: round(dims.w, 4),
144
- h: round(dims.h, 4),
145
- a: round(itm._normalized, 4),
611
+ y: rect.y + rect._iy,
612
+ w: dims.w,
613
+ h: dims.h,
614
+ a: itm._normalized,
146
615
  v: itm.value,
147
616
  s: sum,
148
617
  _data: itm._data
149
- };
618
+ }, dpr);
150
619
  if (itm.group) {
151
620
  r.g = itm.group;
152
621
  r.l = itm.level;
@@ -156,16 +625,16 @@ function buildRow(rect, itm, dims, sum) {
156
625
  }
157
626
 
158
627
  class Rect {
159
- constructor(r) {
160
- const me = this;
628
+ constructor(r, dpr) {
629
+ this.dpr = dpr;
161
630
  r = r || {w: 1, h: 1};
162
- me.rtl = !!r.rtl;
163
- me.x = r.x || r.left || 0;
164
- me.y = r.y || r.top || 0;
165
- me._ix = 0;
166
- me._iy = 0;
167
- me.w = r.w || r.width || (r.right - r.left);
168
- me.h = r.h || r.height || (r.bottom - r.top);
631
+ this.rtl = !!r.rtl;
632
+ this.x = r.x || r.left || 0;
633
+ this.y = r.y || r.top || 0;
634
+ this._ix = 0;
635
+ this._iy = 0;
636
+ this.w = r.w || r.width || (r.right - r.left);
637
+ this.h = r.h || r.height || (r.bottom - r.top);
169
638
  }
170
639
 
171
640
  get area() {
@@ -186,34 +655,61 @@ class Rect {
186
655
  }
187
656
 
188
657
  get side() {
189
- return this.dir === 'x' ? this.iw : this.ih;
658
+ return rasterize(this.dir === 'x' ? this.iw : this.ih, this.dpr);
190
659
  }
191
660
 
192
661
  map(arr) {
193
- const me = this;
194
- const ret = [];
662
+ const {dir, side, dpr} = this;
663
+ const {key, d2prop, id2prop, d2key} = getPropNames(dir);
195
664
  const sum = arr.nsum;
196
665
  const row = arr.get();
197
- const dir = me.dir;
198
- const side = me.side;
199
666
  const w2 = side * side;
200
- const key = dir === 'x' ? '_ix' : '_iy';
667
+ const availd2 = rasterize(this[id2prop], dpr);
201
668
  const s2 = sum * sum;
669
+ const ret = [];
202
670
  let maxd2 = 0;
203
671
  let totd1 = 0;
204
672
  for (const itm of row) {
205
- const dims = getDims(itm, w2, s2, key);
673
+ const dims = getDims(itm, w2, s2, key, dpr, availd2);
206
674
  totd1 += dims.d1;
207
- maxd2 = Math.max(maxd2, dims.d2);
208
- ret.push(buildRow(me, itm, dims, arr.sum));
209
- me[key] += dims.d1;
675
+ maxd2 = Math.min(Math.max(maxd2, dims.d2), availd2);
676
+ ret.push(buildRow(this, itm, dims, arr.sum, dpr));
677
+ this[key] += dims.d1;
210
678
  }
211
- me[dir === 'y' ? '_ix' : '_iy'] += maxd2;
212
- me[key] -= totd1;
679
+ // make sure all rects are same size in d2 direction
680
+ for (const r of ret) {
681
+ const d2 = r[d2prop];
682
+ if (d2 !== maxd2) {
683
+ if (this.rtl && dir === 'y') {
684
+ // for RTL, x-coordinate needs to be adjusted too
685
+ r.x -= maxd2 - d2;
686
+ }
687
+ r[d2prop] = maxd2;
688
+ }
689
+ }
690
+ this[d2key] += maxd2;
691
+ this[key] -= totd1;
213
692
  return ret;
214
693
  }
215
694
  }
216
695
 
696
+ function getPropNames(dir) {
697
+ if (dir === 'x') {
698
+ return {
699
+ key: '_ix',
700
+ d2prop: 'h',
701
+ id2prop: 'ih',
702
+ d2key: '_iy'
703
+ };
704
+ }
705
+ return {
706
+ key: '_iy',
707
+ d2prop: 'w',
708
+ id2prop: 'iw',
709
+ d2key: '_ix'
710
+ };
711
+ }
712
+
217
713
  const min = Math.min;
218
714
  const max = Math.max;
219
715
 
@@ -311,15 +807,16 @@ function compareAspectRatio(oldStat, newStat, args) {
311
807
  *
312
808
  * @param {number[]|object[]} values
313
809
  * @param {object} rectangle
314
- * @param {string} key
315
- * @param {*} grp
316
- * @param {*} lvl
317
- * @param {*} gsum
810
+ * @param {string} [key]
811
+ * @param {number} [dpr=1]
812
+ * @param {*} [grp]
813
+ * @param {*} [lvl]
814
+ * @param {*} [gsum]
318
815
  */
319
- function squarify(values, rectangle, key, grp, lvl, gsum) {
816
+ function squarify(values, rectangle, key, dpr = 1, grp, lvl, gsum) {
320
817
  values = values || [];
321
818
  const rows = [];
322
- const rect = new Rect(rectangle);
819
+ const rect = new Rect(rectangle, dpr);
323
820
  const row = new StatArray('value', rect.area / sum(values, key));
324
821
  let length = rect.side;
325
822
  const n = values.length;
@@ -356,7 +853,7 @@ function squarify(values, rectangle, key, grp, lvl, gsum) {
356
853
  return flatten(rows);
357
854
  }
358
855
 
359
- var version = "2.0.2";
856
+ var version = "2.1.1";
360
857
 
361
858
  function rectNotEqual(r1, r2) {
362
859
  return !r1 || !r2
@@ -381,98 +878,41 @@ function arrayNotEqual(a1, a2) {
381
878
  return false;
382
879
  }
383
880
 
384
- function shouldDrawCaption(rect, font) {
385
- if (!font) {
386
- return false;
387
- }
388
- const w = rect.width || rect.w;
389
- const h = rect.height || rect.h;
390
- const min = font.lineHeight * 2;
391
- return w > min && h > min;
392
- }
393
-
394
- function drawCaption(ctx, rect, item, opts, levels) {
395
- ctx.save();
396
- ctx.beginPath();
397
- ctx.rect(rect.x, rect.y, rect.width, rect.height);
398
- ctx.clip();
399
- if (!('l' in item) || item.l === levels) {
400
- drawLabels(ctx, item, rect);
401
- } else if (opts.captions && opts.captions.display) {
402
- drawCaptionLabel(ctx, item, rect);
403
- }
404
- ctx.restore();
405
- }
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
-
425
- function drawDivider(ctx, rect) {
426
- const opts = rect.options;
427
- const dividersOpts = opts.dividers || {};
428
- const w = rect.width || rect.w;
429
- const h = rect.height || rect.h;
430
-
431
- ctx.save();
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;
437
- ctx.beginPath();
438
- if (w > h) {
439
- const w2 = w / 2;
440
- ctx.moveTo(rect.x + w2, rect.y);
441
- ctx.lineTo(rect.x + w2, rect.y + h);
442
- } else {
443
- const h2 = h / 2;
444
- ctx.moveTo(rect.x, rect.y + h2);
445
- ctx.lineTo(rect.x + w, rect.y + h2);
446
- }
447
- ctx.stroke();
448
- ctx.restore();
449
- }
450
-
451
- function buildData(dataset, mainRect, captions) {
881
+ function buildData(dataset, mainRect, dpr) {
452
882
  const key = dataset.key || '';
883
+ const treeLeafKey = dataset.treeLeafKey || '_leaf';
453
884
  let tree = dataset.tree || [];
885
+ if (helpers.isObject(tree)) {
886
+ tree = normalizeTreeToArray(key, treeLeafKey, tree);
887
+ }
454
888
  const groups = dataset.groups || [];
455
889
  const glen = groups.length;
456
- const sp = helpers.valueOrDefault(dataset.spacing, 0) + helpers.valueOrDefault(dataset.borderWidth, 0);
457
- const captionsFont = captions.font || {};
458
- const font = helpers.toFont(captionsFont);
890
+ const sp = helpers.valueOrDefault(dataset.spacing, 0);
891
+ const captions = dataset.captions || {};
892
+ const font = helpers.toFont(captions.font);
459
893
  const padding = helpers.valueOrDefault(captions.padding, 3);
460
894
 
461
895
  function recur(gidx, rect, parent, gs) {
462
- const g = groups[gidx];
463
- const pg = (gidx > 0) && groups[gidx - 1];
464
- const gdata = group(tree, g, key, pg, parent);
465
- const gsq = squarify(gdata, rect, key, g, gidx, gs);
896
+ const g = getGroupKey(groups[gidx]);
897
+ const pg = (gidx > 0) && getGroupKey(groups[gidx - 1]);
898
+ const gdata = group(tree, g, key, treeLeafKey, pg, parent, groups.filter((item, index) => index <= gidx));
899
+ const gsq = squarify(gdata, rect, key, dpr, g, gidx, gs);
466
900
  const ret = gsq.slice();
467
- let subRect;
468
901
  if (gidx < glen - 1) {
469
902
  gsq.forEach((sq) => {
470
- subRect = {x: sq.x + sp, y: sq.y + sp, w: sq.w - 2 * sp, h: sq.h - 2 * sp};
471
- if (helpers.valueOrDefault(captions.display, true) && shouldDrawCaption(sq, font)) {
903
+ const bw = parseBorderWidth(dataset.borderWidth, sq.w / 2, sq.h / 2);
904
+ const subRect = {
905
+ ...rect,
906
+ x: sq.x + sp + bw.l,
907
+ y: sq.y + sp + bw.t,
908
+ w: sq.w - 2 * sp - bw.l - bw.r,
909
+ h: sq.h - 2 * sp - bw.t - bw.b,
910
+ };
911
+ if (shouldDrawCaption(subRect, captions)) {
472
912
  subRect.y += font.lineHeight + padding * 2;
473
913
  subRect.h -= font.lineHeight + padding * 2;
474
914
  }
475
- ret.push(...recur(gidx + 1, subRect, sq.g, sq.s));
915
+ ret.push(...recur(gidx + 1, rasterizeRect(subRect, dpr), sq.g, sq.s));
476
916
  });
477
917
  }
478
918
  return ret;
@@ -484,54 +924,22 @@ function buildData(dataset, mainRect, captions) {
484
924
 
485
925
  return glen
486
926
  ? recur(0, mainRect)
487
- : squarify(tree, mainRect, key);
927
+ : squarify(tree, mainRect, key, dpr);
488
928
  }
489
929
 
490
- function drawLabels(ctx, item, rect) {
491
- const opts = rect.options;
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};
930
+ function getMinMax(data, useTree) {
931
+ const vMax = useTree ? 1 : maxValue(data);
932
+ const vMin = useTree ? 0 : minValue(data, vMax);
933
+ return {vMin, vMax};
526
934
  }
527
935
 
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;
936
+ function getArea({xScale, yScale}, data, rtl, useTree) {
937
+ const {vMin, vMax} = getMinMax(data, useTree);
938
+ const xMin = xScale.getPixelForValue(0);
939
+ const xMax = xScale.getPixelForValue(1);
940
+ const yMin = yScale.getPixelForValue(vMin);
941
+ const yMax = yScale.getPixelForValue(vMax);
942
+ return {x: xMin, y: yMax, w: xMax - xMin, h: yMin - yMax, rtl};
535
943
  }
536
944
 
537
945
  class TreemapController extends chart_js.DatasetController {
@@ -541,6 +949,7 @@ class TreemapController extends chart_js.DatasetController {
541
949
  this._rect = undefined;
542
950
  this._key = undefined;
543
951
  this._groups = undefined;
952
+ this._useTree = undefined;
544
953
  }
545
954
 
546
955
  initialize() {
@@ -548,38 +957,52 @@ class TreemapController extends chart_js.DatasetController {
548
957
  super.initialize();
549
958
  }
550
959
 
960
+ /**
961
+ * @todo: Remove with https://github.com/kurkle/chartjs-chart-treemap/issues/137
962
+ */
963
+ updateRangeFromParsed(range, scale) {
964
+ if (range.updated) {
965
+ return;
966
+ }
967
+ range.updated = true;
968
+ if (scale.axis === 'x') {
969
+ range.min = 0;
970
+ range.max = 1;
971
+ return;
972
+ }
973
+ const dataset = this.getDataset();
974
+ const {vMin, vMax} = getMinMax(dataset.data, this._useTree);
975
+ range.min = vMin;
976
+ range.max = vMax;
977
+ }
978
+
551
979
  update(mode) {
552
- const me = this;
553
- const meta = me.getMeta();
554
- const dataset = me.getDataset();
980
+ const meta = this.getMeta();
981
+ const dataset = this.getDataset();
982
+ const dpr = this.chart.currentDevicePixelRatio;
983
+
984
+ if (!helpers.defined(this._useTree)) {
985
+ this._useTree = !!dataset.tree;
986
+ }
555
987
  const groups = dataset.groups || (dataset.groups = []);
556
- const captions = dataset.captions ? dataset.captions : {};
557
- const area = me.chart.chartArea;
558
988
  const key = dataset.key || '';
559
989
  const rtl = !!dataset.rtl;
560
990
 
561
- const mainRect = {x: area.left, y: area.top, w: area.right - area.left, h: area.bottom - area.top, rtl};
991
+ const mainRect = rasterizeRect(getArea(meta, dataset.data, rtl, this._useTree), dpr);
562
992
 
563
- if (mode === 'reset' || rectNotEqual(me._rect, mainRect) || me._key !== key || arrayNotEqual(me._groups, groups)) {
564
- me._rect = mainRect;
565
- me._groups = groups.slice();
566
- me._key = key;
993
+ if (mode === 'reset' || rectNotEqual(this._rect, mainRect) || this._key !== key || arrayNotEqual(this._groups, groups)) {
994
+ this._rect = mainRect;
995
+ this._groups = groups.slice();
996
+ this._key = key;
567
997
 
568
- dataset.data = buildData(dataset, mainRect, captions);
998
+ dataset.data = buildData(dataset, mainRect, dpr);
569
999
  // @ts-ignore using private stuff
570
- me._dataCheck();
1000
+ this._dataCheck();
571
1001
  // @ts-ignore using private stuff
572
- me._resyncElements();
1002
+ this._resyncElements();
573
1003
  }
574
1004
 
575
- me.updateElements(meta.data, 0, meta.data.length, mode);
576
- }
577
-
578
- resolveDataElementOptions(index, mode) {
579
- const options = super.resolveDataElementOptions(index, mode);
580
- const result = Object.isFrozen(options) ? Object.assign({}, options) : options;
581
- result.font = helpers.toFont(options.captions.font);
582
- return result;
1005
+ this.updateElements(meta.data, 0, meta.data.length, mode);
583
1006
  }
584
1007
 
585
1008
  updateElements(rects, start, count, mode) {
@@ -612,41 +1035,21 @@ class TreemapController extends chart_js.DatasetController {
612
1035
  me.updateSharedOptions(sharedOptions, mode, firstOpts);
613
1036
  }
614
1037
 
615
- _drawDividers(ctx, data, metadata) {
616
- for (let i = 0, ilen = metadata.length; i < ilen; ++i) {
617
- const rect = metadata[i];
618
- const item = data[i];
619
- const dividersOpts = rect.options.dividers || {};
620
- if (dividersOpts.display && item._data.children.length > 1) {
621
- drawDivider(ctx, rect);
622
- }
623
- }
624
- }
1038
+ draw() {
1039
+ const {ctx, chartArea, currentDevicePixelRatio: dpr} = this.chart;
1040
+ const metadata = this.getMeta().data || [];
1041
+ const dataset = this.getDataset();
1042
+ const levels = (dataset.groups || []).length - 1;
1043
+ const data = dataset.data;
625
1044
 
626
- _drawRects(ctx, data, metadata, levels) {
1045
+ helpers.clipArea(ctx, chartArea);
627
1046
  for (let i = 0, ilen = metadata.length; i < ilen; ++i) {
628
1047
  const rect = metadata[i];
629
- const item = data[i];
630
1048
  if (!rect.hidden) {
631
- rect.draw(ctx);
632
- const opts = rect.options;
633
- if (shouldDrawCaption(rect, opts.captions.font)) {
634
- drawCaption(ctx, rect, item, opts, levels);
635
- }
1049
+ rect.draw(ctx, data[i], levels, dpr);
636
1050
  }
637
1051
  }
638
- }
639
-
640
- draw() {
641
- const me = this;
642
- const ctx = me.chart.ctx;
643
- const metadata = me.getMeta().data || [];
644
- const dataset = me.getDataset();
645
- const levels = (dataset.groups || []).length - 1;
646
- const data = dataset.data || [];
647
-
648
- me._drawRects(ctx, data, metadata, levels);
649
- me._drawDividers(ctx, data, metadata);
1052
+ helpers.unclipArea(ctx);
650
1053
  }
651
1054
  }
652
1055
 
@@ -664,13 +1067,6 @@ TreemapController.defaults = {
664
1067
  },
665
1068
  },
666
1069
 
667
- borderWidth: 0,
668
- spacing: 0.5,
669
- dividers: {
670
- display: false,
671
- lineWidth: 1,
672
- }
673
-
674
1070
  };
675
1071
 
676
1072
  TreemapController.descriptors = {
@@ -681,6 +1077,7 @@ TreemapController.descriptors = {
681
1077
  TreemapController.overrides = {
682
1078
  interaction: {
683
1079
  mode: 'point',
1080
+ includeInvisible: true,
684
1081
  intersect: true
685
1082
  },
686
1083
 
@@ -701,7 +1098,7 @@ TreemapController.overrides = {
701
1098
  label(item) {
702
1099
  const dataset = item.dataset;
703
1100
  const dataItem = dataset.data[item.dataIndex];
704
- const label = dataItem.g || dataset.label;
1101
+ const label = dataItem.g || dataItem._data.label || dataset.label;
705
1102
  return (label ? label + ': ' : '') + dataItem.v;
706
1103
  }
707
1104
  }
@@ -720,7 +1117,7 @@ TreemapController.overrides = {
720
1117
  };
721
1118
 
722
1119
  TreemapController.beforeRegister = function() {
723
- requireVersion('3.6', chart_js.Chart.version);
1120
+ requireVersion('3.8', chart_js.Chart.version);
724
1121
  };
725
1122
 
726
1123
  TreemapController.afterRegister = function() {
@@ -736,6 +1133,8 @@ TreemapController.afterRegister = function() {
736
1133
 
737
1134
  return el.tooltipPosition();
738
1135
  };
1136
+ } else {
1137
+ console.warn('Unable to register the treemap positioner because tooltip plugin is not registered');
739
1138
  }
740
1139
  };
741
1140
 
@@ -746,189 +1145,15 @@ TreemapController.afterUnregister = function() {
746
1145
  }
747
1146
  };
748
1147
 
749
- /**
750
- * Helper function to get the bounds of the rect
751
- * @param {TreemapElement} rect the rect
752
- * @param {boolean} [useFinalPosition]
753
- * @return {object} bounds of the rect
754
- * @private
755
- */
756
- function getBounds(rect, useFinalPosition) {
757
- const {x, y, width, height} = rect.getProps(['x', 'y', 'width', 'height'], useFinalPosition);
758
- return {left: x, top: y, right: x + width, bottom: y + height};
759
- }
760
-
761
- function limit(value, min, max) {
762
- return Math.max(Math.min(value, max), min);
763
- }
764
-
765
- function parseBorderWidth(value, maxW, maxH) {
766
- let t, r, b, l;
767
-
768
- if (isObject(value)) {
769
- t = +value.top || 0;
770
- r = +value.right || 0;
771
- b = +value.bottom || 0;
772
- l = +value.left || 0;
773
- } else {
774
- t = r = b = l = +value || 0;
775
- }
776
-
777
- return {
778
- t: limit(t, 0, maxH),
779
- r: limit(r, 0, maxW),
780
- b: limit(b, 0, maxH),
781
- l: limit(l, 0, maxW)
782
- };
783
- }
784
-
785
- function boundingRects(rect) {
786
- const bounds = getBounds(rect);
787
- const width = bounds.right - bounds.left;
788
- const height = bounds.bottom - bounds.top;
789
- const border = parseBorderWidth(rect.options.borderWidth, width / 2, height / 2);
790
-
791
- return {
792
- outer: {
793
- x: bounds.left,
794
- y: bounds.top,
795
- w: width,
796
- h: height
797
- },
798
- inner: {
799
- x: bounds.left + border.l,
800
- y: bounds.top + border.t,
801
- w: width - border.l - border.r,
802
- h: height - border.t - border.b
803
- }
804
- };
805
- }
806
-
807
- function inRange(rect, x, y, useFinalPosition) {
808
- const skipX = x === null;
809
- const skipY = y === null;
810
- const bounds = !rect || (skipX && skipY) ? false : getBounds(rect, useFinalPosition);
811
-
812
- return bounds
813
- && (skipX || x >= bounds.left && x <= bounds.right)
814
- && (skipY || y >= bounds.top && y <= bounds.bottom);
815
- }
816
-
817
- class TreemapElement extends chart_js.Element {
818
-
819
- constructor(cfg) {
820
- super();
821
-
822
- this.options = undefined;
823
- this.width = undefined;
824
- this.height = undefined;
825
-
826
- if (cfg) {
827
- Object.assign(this, cfg);
828
- }
829
- }
830
-
831
- draw(ctx) {
832
- const options = this.options;
833
- const {inner, outer} = boundingRects(this);
834
-
835
- ctx.save();
836
-
837
- if (outer.w !== inner.w || outer.h !== inner.h) {
838
- ctx.beginPath();
839
- ctx.rect(outer.x, outer.y, outer.w, outer.h);
840
- ctx.clip();
841
- ctx.rect(inner.x, inner.y, inner.w, inner.h);
842
- ctx.fillStyle = options.backgroundColor;
843
- ctx.fill();
844
- ctx.fillStyle = options.borderColor;
845
- ctx.fill('evenodd');
846
- } else {
847
- ctx.fillStyle = options.backgroundColor;
848
- ctx.fillRect(inner.x, inner.y, inner.w, inner.h);
849
- }
850
- ctx.restore();
851
- }
852
-
853
- inRange(mouseX, mouseY, useFinalPosition) {
854
- return inRange(this, mouseX, mouseY, useFinalPosition);
855
- }
856
-
857
- inXRange(mouseX, useFinalPosition) {
858
- return inRange(this, mouseX, null, useFinalPosition);
859
- }
860
-
861
- inYRange(mouseY, useFinalPosition) {
862
- return inRange(this, null, mouseY, useFinalPosition);
863
- }
864
-
865
- getCenterPoint(useFinalPosition) {
866
- const {x, y, width, height} = this.getProps(['x', 'y', 'width', 'height'], useFinalPosition);
867
- return {
868
- x: x + width / 2,
869
- y: y + height / 2
870
- };
871
- }
872
-
873
- tooltipPosition() {
874
- return this.getCenterPoint();
875
- }
876
-
877
- getRange(axis) {
878
- return axis === 'x' ? this.width / 2 : this.height / 2;
879
- }
880
- }
881
-
882
- TreemapElement.id = 'treemap';
883
-
884
- TreemapElement.defaults = {
885
- borderWidth: undefined,
886
- spacing: undefined,
887
- label: 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
919
- };
920
-
921
- TreemapElement.defaultRoutes = {
922
- backgroundColor: 'backgroundColor',
923
- borderColor: 'borderColor'
924
- };
925
-
926
1148
  chart_js.Chart.register(TreemapController, TreemapElement);
927
1149
 
928
1150
  exports.flatten = flatten;
1151
+ exports.getGroupKey = getGroupKey;
929
1152
  exports.group = group;
930
1153
  exports.index = index;
931
- exports.isObject = isObject;
1154
+ exports.maxValue = maxValue;
1155
+ exports.minValue = minValue;
1156
+ exports.normalizeTreeToArray = normalizeTreeToArray;
932
1157
  exports.requireVersion = requireVersion;
933
1158
  exports.sort = sort;
934
1159
  exports.sum = sum;