chartjs-chart-treemap 3.1.0 → 4.0.0

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