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