@syncfusion/ej2-treemap 30.2.4 → 31.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/ej2-treemap.min.js +3 -3
  2. package/dist/ej2-treemap.umd.min.js +3 -3
  3. package/dist/ej2-treemap.umd.min.js.map +1 -1
  4. package/dist/es6/ej2-treemap.es2015.js +0 -4
  5. package/dist/es6/ej2-treemap.es2015.js.map +1 -1
  6. package/dist/es6/ej2-treemap.es5.js +0 -4
  7. package/dist/es6/ej2-treemap.es5.js.map +1 -1
  8. package/dist/global/ej2-treemap.min.js +3 -3
  9. package/dist/global/ej2-treemap.min.js.map +1 -1
  10. package/dist/global/index.d.ts +2 -2
  11. package/dist/ts/index.d.ts +4 -0
  12. package/dist/ts/index.ts +4 -0
  13. package/dist/ts/treemap/index.d.ts +19 -0
  14. package/dist/ts/treemap/index.ts +19 -0
  15. package/dist/ts/treemap/layout/legend.d.ts +137 -0
  16. package/dist/ts/treemap/layout/legend.ts +1095 -0
  17. package/dist/ts/treemap/layout/render-panel.d.ts +47 -0
  18. package/dist/ts/treemap/layout/render-panel.ts +758 -0
  19. package/dist/ts/treemap/model/base-model.d.ts +795 -0
  20. package/dist/ts/treemap/model/base.d.ts +671 -0
  21. package/dist/ts/treemap/model/base.ts +798 -0
  22. package/dist/ts/treemap/model/constants.d.ts +117 -0
  23. package/dist/ts/treemap/model/constants.ts +118 -0
  24. package/dist/ts/treemap/model/image-export.d.ts +34 -0
  25. package/dist/ts/treemap/model/image-export.ts +117 -0
  26. package/dist/ts/treemap/model/interface.d.ts +555 -0
  27. package/dist/ts/treemap/model/interface.ts +583 -0
  28. package/dist/ts/treemap/model/pdf-export.d.ts +36 -0
  29. package/dist/ts/treemap/model/pdf-export.ts +105 -0
  30. package/dist/ts/treemap/model/print.d.ts +45 -0
  31. package/dist/ts/treemap/model/print.ts +106 -0
  32. package/dist/ts/treemap/model/theme.d.ts +19 -0
  33. package/dist/ts/treemap/model/theme.ts +450 -0
  34. package/dist/ts/treemap/treemap-model.d.ts +374 -0
  35. package/dist/ts/treemap/treemap.d.ts +724 -0
  36. package/dist/ts/treemap/treemap.ts +1817 -0
  37. package/dist/ts/treemap/user-interaction/highlight-selection.d.ts +118 -0
  38. package/dist/ts/treemap/user-interaction/highlight-selection.ts +799 -0
  39. package/dist/ts/treemap/user-interaction/tooltip.d.ts +42 -0
  40. package/dist/ts/treemap/user-interaction/tooltip.ts +228 -0
  41. package/dist/ts/treemap/utils/enum.d.ts +256 -0
  42. package/dist/ts/treemap/utils/enum.ts +263 -0
  43. package/dist/ts/treemap/utils/helper.d.ts +543 -0
  44. package/dist/ts/treemap/utils/helper.ts +1453 -0
  45. package/package.json +52 -17
  46. package/src/treemap/treemap.d.ts +0 -1
  47. package/src/treemap/treemap.js +0 -4
@@ -0,0 +1,758 @@
1
+ import { TreeMap } from '../treemap';
2
+ import {
3
+ Rect, itemsToOrder, TextOption, Size, measureText, textTrim, hide, wordWrap, textWrap,
4
+ getTemplateFunction, convertElement, findLabelLocation, PathOption, textFormatter, ColorValue, colorNameToHex, convertHexToColor,
5
+ colorMap, measureElement, convertToContainer, convertToRect, getShortestEdge, getArea, orderByArea, isParentItem, maintainSelection
6
+ } from '../utils/helper';
7
+ import { isNullOrUndefined, createElement, extend, SanitizeHtmlHelper } from '@syncfusion/ej2-base';
8
+ import { SvgRenderer } from '@syncfusion/ej2-svg-base';
9
+ import { Location, findChildren, renderTextElement } from '../utils/helper';
10
+ import { LevelSettings, LeafItemSettings } from '../model/base';
11
+ import { LabelPosition, LabelAlignment } from '../utils/enum';
12
+ import { BorderModel, FontModel, ColorMappingModel, LevelSettingsModel, LeafItemSettingsModel } from '../model/base-model';
13
+ import { IItemRenderingEventArgs } from '../model/interface';
14
+ import { itemRendering } from '../model/constants';
15
+
16
+ /**
17
+ * To calculate and render the shape layer
18
+ */
19
+
20
+ export class LayoutPanel {
21
+ private treemap: TreeMap;
22
+ private currentRect: Rect;
23
+ public layoutGroup: Element;
24
+ private renderer: SvgRenderer;
25
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
26
+ public renderItems: any[];
27
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
28
+ private parentData: any[];
29
+ constructor(treemap: TreeMap) {
30
+ this.treemap = treemap;
31
+ }
32
+
33
+ public processLayoutPanel(): void {
34
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
35
+ let data: any[] | any; let totalRect: Rect;
36
+ if (this.treemap.treemapLevelData.levelsData && this.treemap.treemapLevelData.levelsData.length > 0) {
37
+ data = (!isNullOrUndefined(this.treemap.initialDrillDown.groupIndex) &&
38
+ !isNullOrUndefined(this.treemap.initialDrillDown.groupName)) &&
39
+ (isNullOrUndefined(this.treemap.drilledItems) ? isNullOrUndefined(this.treemap.drilledItems)
40
+ : this.treemap.drilledItems.length === 0) ?
41
+ this.getDrilldownData(this.treemap.treemapLevelData.levelsData[0], [])[0] : this.treemap.treemapLevelData.levelsData[0];
42
+ totalRect = extend({}, this.treemap.areaRect, totalRect, false) as Rect;
43
+ if (!isNullOrUndefined(this.treemap.treeMapLegendModule) && !isNullOrUndefined(this.treemap.totalRect)) {
44
+ if (this.treemap.legendSettings.position !== 'Float') {
45
+ totalRect = this.treemap.totalRect;
46
+ }
47
+ }
48
+ if (!isNullOrUndefined(this.treemap.currentLevel) &&
49
+ (isNullOrUndefined(this.treemap.drilledItems) ? !isNullOrUndefined(this.treemap.drilledItems)
50
+ : this.treemap.drilledItems.length !== 0)) {
51
+ const count: number = this.treemap.drilledItems.length - 1;
52
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
53
+ const x: any = this.treemap.drilledItems[count as number]['data'];
54
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
55
+ const y: any = {};
56
+ y[this.treemap.drilledItems[count as number]['data']['groupName']] = [x];
57
+ if (!isNullOrUndefined(this.treemap.initialDrillDown.groupIndex) && !this.treemap.enableBreadcrumb) {
58
+ this.treemap.currentLevel = this.treemap.drilledItems[count as number]['data']['groupIndex'];
59
+ }
60
+ this.calculateLayoutItems(y || this.treemap.treemapLevelData.levelsData[0], totalRect);
61
+ this.renderLayoutItems();
62
+ } else {
63
+ if (!isNullOrUndefined(this.treemap.initialDrillDown.groupIndex) &&
64
+ (isNullOrUndefined(this.treemap.drilledItems) ? isNullOrUndefined(this.treemap.drilledItems)
65
+ : this.treemap.drilledItems.length === 0)) {
66
+ this.treemap.currentLevel = this.treemap.initialDrillDown.groupIndex;
67
+ }
68
+ this.calculateLayoutItems(data || this.treemap.treemapLevelData.levelsData[0], totalRect);
69
+ this.renderLayoutItems();
70
+ }
71
+ }
72
+ }
73
+
74
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
75
+ private getDrilldownData(data: any, drillData: any[]): any {
76
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
77
+ const treemap: TreeMap = this.treemap; const newData: any = {};
78
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
79
+ const child: any[] = findChildren(data)['values'];
80
+ if (child && child.length > 0 && drillData.length === 0) {
81
+ for (let i: number = 0; i < child.length; i++) {
82
+ if (child[i as number]['groupIndex'] === treemap.initialDrillDown.groupIndex &&
83
+ child[i as number]['name'] === treemap.initialDrillDown.groupName) {
84
+ child[i as number]['isDrilled'] = true;
85
+ newData[child[i as number]['groupName']] = [child[i as number]];
86
+ drillData.push(newData);
87
+ }
88
+ }
89
+ for (let j: number = 0; j < child.length; j++) {
90
+ this.getDrilldownData(child[j as number], drillData);
91
+ }
92
+ }
93
+ return drillData;
94
+ }
95
+
96
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
97
+ public calculateLayoutItems(data: any, rect: Rect): void {
98
+ this.renderItems = [];
99
+ this.parentData = [];
100
+ if (!isNullOrUndefined(this.treemap.weightValuePath)) {
101
+ if (this.treemap.layoutType.indexOf('SliceAndDice') > -1) {
102
+ this.computeSliceAndDiceDimensional(data, rect);
103
+ } else {
104
+ rect.height = rect.height + rect.y;
105
+ rect.width = rect.width + rect.x;
106
+ this.computeSquarifyDimensional(data, rect);
107
+ }
108
+ }
109
+ }
110
+
111
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
112
+ private computeSliceAndDiceDimensional(data: any, coords: Rect): any {
113
+ const leafItem: LeafItemSettings = this.treemap.leafItemSettings as LeafItemSettings;
114
+ let rect: Rect; const groups: LevelSettings[] = this.treemap.levels as LevelSettings[];
115
+ let groupIndex: number; let isLeafItem: boolean = false;
116
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
117
+ const children: any[] = findChildren(data)['values'];
118
+ let gap: number; let headerHeight: number;
119
+ if (children && children.length > 0) {
120
+ this.sliceAndDiceProcess(children, coords);
121
+ if (this.treemap.levels.length > 0) {
122
+ for (let i: number = 0; i < children.length; i++) {
123
+ groupIndex = children[i as number]['groupIndex'];
124
+ isLeafItem = (groups.length === 0 || groupIndex === groups.length);
125
+ gap = isLeafItem ? leafItem.gap : groups[groupIndex as number].groupGap;
126
+ headerHeight = groups.length === 0 ? 0 : groups[groupIndex as number] ? groups[groupIndex as number].showHeader ?
127
+ groups[groupIndex as number].headerHeight : 0 : groups[groupIndex - 1].showHeader ?
128
+ groups[groupIndex - 1].headerHeight : 0;
129
+ rect = children[i as number]['rect'];
130
+ rect = new Rect(
131
+ rect.x + (gap / 2), rect.y + (headerHeight + (gap / 2)), rect.width - gap,
132
+ Math.abs(rect.height - (gap + headerHeight)));
133
+ this.computeSliceAndDiceDimensional(children[i as number], rect);
134
+ }
135
+ }
136
+ }
137
+ return data;
138
+ }
139
+
140
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
141
+ private sliceAndDiceProcess(processData: any[], rect: Rect): void {
142
+ const parentArea: number = rect.height * rect.width;
143
+ const levels: LevelSettingsModel[] = this.treemap.levels;
144
+ let childValue: number; let alottedValue: number = 0; let totalWeight: number = 0;
145
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
146
+ processData.forEach((data: any) => { totalWeight += data['weight']; });
147
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
148
+ processData.forEach((child: any) => {
149
+ child['weightArea'] = parentArea * (child['weight'] as number) / totalWeight;
150
+ });
151
+ const isHorizontal: boolean = (this.treemap.layoutType === 'SliceAndDiceAuto') ? (rect.width > rect.height) :
152
+ (this.treemap.layoutType === 'SliceAndDiceHorizontal');
153
+ processData.sort(itemsToOrder);
154
+ for (let i: number = 0; i < processData.length; i++) {
155
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
156
+ const item: any = processData[i as number];
157
+ item['isLeafItem'] = (levels.length === 0) || ((this.treemap.isHierarchicalData ||
158
+ isNullOrUndefined(this.treemap.leafItemSettings.labelPath)) ?
159
+ item['groupIndex'] === levels.length - 1 : item['groupIndex'] === this.treemap.levels.length);
160
+ if (isHorizontal) {
161
+ childValue = ((parentArea / totalWeight) * processData[i as number]['weight']) / rect.height;
162
+ if (alottedValue <= rect.width) {
163
+ processData[i as number]['rect'] = new Rect(alottedValue + rect.x, rect.y, childValue, rect.height);
164
+ }
165
+ } else {
166
+ childValue = ((parentArea / totalWeight) * processData[i as number]['weight']) / rect.width;
167
+ if (alottedValue <= rect.height) {
168
+ processData[i as number]['rect'] = new Rect(rect.x, alottedValue + rect.y, rect.width, childValue);
169
+ }
170
+ }
171
+ alottedValue += childValue;
172
+ this.renderItems.push(processData[i as number]);
173
+ }
174
+ }
175
+
176
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
177
+ private computeSquarifyDimensional(data: any, coords: Rect): void {
178
+ const leaf: LeafItemSettings = this.treemap.leafItemSettings as LeafItemSettings;
179
+ let rect: Rect; const levels: LevelSettings[] = this.treemap.levels as LevelSettings[];
180
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
181
+ let item: any;
182
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
183
+ const child: any[] = findChildren(data)['values']; let index: number;
184
+ let padding: number; let headerHeight: number;
185
+ if (child && child.length > 0) {
186
+ if (this.parentData.length === 0) {
187
+ this.parentData = [];
188
+ this.parentData.push(child);
189
+ }
190
+ this.calculateChildrenLayout(data, child, coords);
191
+ if (this.treemap.levels.length > 0) {
192
+ for (let i: number = 0; i < child.length; i++) {
193
+ item = child[i as number];
194
+ index = item['groupIndex'];
195
+ rect = item['rect'];
196
+ padding = (item['isLeafItem'] ? leaf.padding : levels[index as number].groupPadding) / 2;
197
+ headerHeight = this.treemap.isHierarchicalData ? index === 0 && item['isLeafItem'] ? 0 : levels[index as number] ?
198
+ levels[index as number].showHeader ? levels[index as number].headerHeight : 0 : 0 :
199
+ (levels.length === 0) ? 0 : levels[index as number] ?
200
+ levels[index as number].showHeader ? levels[index as number].headerHeight : 0 : 0;
201
+ rect = new Rect(
202
+ rect.x + padding, rect.y + (headerHeight + padding),
203
+ rect.width - padding, rect.height - padding
204
+ );
205
+ if (!item['isLeafItem'] && item['weight'] > 0) {
206
+ this.computeSquarifyDimensional(child[i as number], rect);
207
+ }
208
+ }
209
+ }
210
+ }
211
+ }
212
+
213
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
214
+ private calculateChildrenLayout(parent: any, children: any[], coords: Rect): void {
215
+ this.computeTotalArea(children, getArea(coords));
216
+ children.sort(orderByArea);
217
+ this.performRowsLayout(children, [], coords, []);
218
+ }
219
+
220
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
221
+ private performRowsLayout(data: any[], currentRow: any[], rect: Rect, stack: any[]): any[] {
222
+ const dataLength: number = data.length;
223
+ if (dataLength === 0) {
224
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
225
+ const newCoordinates: any[] = this.getCoordinates(currentRow, rect);
226
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
227
+ const newStack: any[] = stack.concat(newCoordinates);
228
+ return newStack;
229
+ }
230
+ const width: number = getShortestEdge(rect);
231
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
232
+ const nextDatum: any = data[0];
233
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
234
+ const restData: any[] = data.slice(1, dataLength);
235
+ if (this.aspectRatio(currentRow, nextDatum, width)) {
236
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
237
+ const newRow: any[] = currentRow.concat(nextDatum);
238
+ return this.performRowsLayout(restData, newRow, rect, stack);
239
+ } else {
240
+ const currentRowLength: number = currentRow.length;
241
+ let valueSum: number = 0;
242
+ for (let i: number = 0; i < currentRowLength; i += 1) {
243
+ valueSum += currentRow[i as number]['itemArea'];
244
+ }
245
+ const newContainer: Rect = this.cutArea(rect, valueSum);
246
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
247
+ const newCoordinates: any[] = this.getCoordinates(currentRow, rect);
248
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
249
+ const newStack: any[] = stack.concat(newCoordinates);
250
+ return this.performRowsLayout(data, [], newContainer, newStack);
251
+ }
252
+ }
253
+
254
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
255
+ private aspectRatio(currentRow: any[], nextDatum: any, length: number): boolean {
256
+ if (currentRow.length === 0) {
257
+ return true;
258
+ } else {
259
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
260
+ const newRow: any[] = currentRow.concat(nextDatum);
261
+ const currentMaxAspectRatio: number = this.findMaxAspectRatio(currentRow, length);
262
+ const newMaxAspectRatio: number = this.findMaxAspectRatio(newRow, length);
263
+ return (currentMaxAspectRatio >= newMaxAspectRatio);
264
+ }
265
+ }
266
+
267
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
268
+ private findMaxAspectRatio(row: any[], length: number): number {
269
+ const rowLength: number = row.length;
270
+ let minArea: number = Infinity;
271
+ let maxArea: number = -Infinity;
272
+ let sumArea: number = 0;
273
+ for (let i: number = 0; i < rowLength; i += 1) {
274
+ const area: number = row[i as number]['itemArea'];
275
+ if (area < minArea) {
276
+ minArea = area;
277
+ }
278
+ if (area > maxArea) {
279
+ maxArea = area;
280
+ }
281
+ sumArea += area;
282
+ }
283
+ const result: number = Math.max((Math.pow(length, 2)) * maxArea / (Math.pow(sumArea, 2)), (Math.pow(sumArea, 2)) /
284
+ ((Math.pow(length, 2)) * minArea));
285
+ return result;
286
+ }
287
+
288
+
289
+ private cutArea(rect: Rect, area: number): Rect {
290
+ const newContainer: Rect = convertToContainer(rect);
291
+ const width: number = newContainer.width;
292
+ const height: number = newContainer.height;
293
+ const xOffset: number = newContainer.x;
294
+ const yOffset: number = newContainer.y;
295
+ if (width >= height) {
296
+ const areaWidth: number = area / height;
297
+ const newWidth: number = width - areaWidth;
298
+ const container: Rect = {
299
+ x: xOffset + areaWidth,
300
+ y: yOffset,
301
+ width: newWidth,
302
+ height: height
303
+ };
304
+ return convertToRect(container);
305
+ } else {
306
+ const areaHeight: number = area / width;
307
+ const newHeight: number = height - areaHeight;
308
+ const container: Rect = {
309
+ x: xOffset,
310
+ y: yOffset + areaHeight,
311
+ width: width,
312
+ height: newHeight
313
+ };
314
+ return convertToRect(container);
315
+ }
316
+ }
317
+
318
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
319
+ private getCoordinates(row: any[], rect: Rect): any[] {
320
+ const container: Rect = convertToContainer(rect);
321
+ const width: number = container.width; const height: number = container.height;
322
+ const xOffset: number = container.x; const yOffset: number = container.y;
323
+ const rowLength: number = row.length; const levels: LevelSettingsModel[] = this.treemap.levels;
324
+ const leaf: LeafItemSettingsModel = this.treemap.leafItemSettings; let index: number;
325
+ let valueSum: number = 0;
326
+ for (let i: number = 0; i < rowLength; i += 1) {
327
+ valueSum += row[i as number]['itemArea'];
328
+ }
329
+ const areaWidth: number = valueSum / height; const areaHeight: number = valueSum / width;
330
+ let subXOffset: number = xOffset; let subYOffset: number = yOffset; let padding: number;
331
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
332
+ const coordinates: any[] = []; let isParent: boolean; let parentRect: Rect;
333
+ for (let i: number = 0; i < rowLength; i += 1) {
334
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
335
+ const item: any = row[i as number];
336
+ index = item['groupIndex'];
337
+ item['isLeafItem'] = (levels.length === 0) || (this.treemap.isHierarchicalData ? index === levels.length :
338
+ isNullOrUndefined(leaf.labelPath) ? false : index === levels.length);
339
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
340
+ isParent = isParentItem(this.parentData[0] as any[], item);
341
+ parentRect = isParent ? this.treemap.areaRect : item['parent'].rect;
342
+ padding = item['isLeafItem'] ? leaf.padding : levels[index as number].groupPadding;
343
+ if (width >= height) {
344
+ const y1: number = subYOffset + item['itemArea'] / areaWidth;
345
+ item['rect'] = {
346
+ x: subXOffset,
347
+ y: subYOffset,
348
+ width: subXOffset + areaWidth,
349
+ height: y1
350
+ };
351
+ subYOffset = y1;
352
+ } else {
353
+ const x1: number = subXOffset + item['itemArea'] / areaHeight;
354
+ item['rect'] = {
355
+ x: subXOffset,
356
+ y: subYOffset,
357
+ width: x1,
358
+ height: subYOffset + areaHeight
359
+ };
360
+ subXOffset = x1;
361
+ }
362
+ if (item['weight'] > 0 && (isParent || (Math.round(rect.y + (padding / 2)) <=
363
+ Math.round(parentRect.y + (parentRect.height - parentRect.y)) && Math.round(rect.x + (padding / 2)) <=
364
+ Math.round(parentRect.x + (parentRect.width - parentRect.x))))) {
365
+ this.renderItems.push(item);
366
+ coordinates.push(item);
367
+ }
368
+ }
369
+ return coordinates;
370
+ }
371
+
372
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
373
+ private computeTotalArea(data: any[], area: number): any[] {
374
+ const dataLength: number = data.length;
375
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
376
+ const result: any[] = [];
377
+ for (let i: number = 0; i < dataLength; i += 1) {
378
+ const dataLength: number = data.length;
379
+ let dataSum: number = 0;
380
+ for (let i: number = 0; i < dataLength; i += 1) {
381
+ dataSum += data[i as number]['weight'];
382
+ }
383
+ const multiplier: number = area / dataSum;
384
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
385
+ let datum: any;
386
+ for (let j: number = 0; j < dataLength; j++) {
387
+ datum = data[j as number];
388
+ datum['itemArea'] = datum['weight'] * multiplier;
389
+ result.push(datum);
390
+ }
391
+ }
392
+ return result;
393
+ }
394
+
395
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
396
+ public onDemandProcess(childItems: any): void {
397
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
398
+ let parentItem: any = {}; let totalRect: Rect;
399
+ parentItem = childItems[0]['parent'];
400
+ this.treemap.currentLevel = parentItem['isDrilled'] ? parentItem['groupIndex'] : null;
401
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
402
+ let parentItemGroupname: any = {};
403
+ if (isNullOrUndefined(parentItem['groupName'])) {
404
+ parentItemGroupname = parentItem;
405
+ } else {
406
+ parentItemGroupname[parentItem['groupName']] = [parentItem];
407
+ }
408
+ totalRect = extend({}, this.treemap.areaRect, totalRect, false) as Rect;
409
+ if (!isNullOrUndefined(this.treemap.treeMapLegendModule) && !isNullOrUndefined(this.treemap.totalRect)) {
410
+ totalRect = this.treemap.totalRect;
411
+ }
412
+ const count: number = this.treemap.levels.length;
413
+ for (let i: number = 0; i < count; i++) {
414
+ const levelCount: number = childItems[0]['groupIndex'];
415
+ if (count === levelCount) {
416
+ this.treemap.levels[count as number] = this.treemap.levels[i as number];
417
+ } else {
418
+ this.treemap.levels.splice(count - 1, 1);
419
+ }
420
+ }
421
+ this.calculateLayoutItems(parentItemGroupname, totalRect);
422
+ this.renderLayoutItems();
423
+ }
424
+ // eslint-disable-next-line valid-jsdoc
425
+ /** @private */
426
+ public renderLayoutItems(): void {
427
+ let position: string;
428
+ const treeMap: TreeMap = this.treemap;
429
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
430
+ let txtVisible: boolean; let getItemColor: any; let eventArgs: IItemRenderingEventArgs;
431
+ this.renderer = treeMap.renderer;
432
+ let pathOptions: PathOption;
433
+ const elementID: string = treeMap.element.id; let index: number; let templatePosition: LabelPosition;
434
+ const mode: string = treeMap.layoutType; let rect: Rect; let format: string;
435
+ const interSectAction: LabelAlignment = this.treemap.leafItemSettings.interSectAction;
436
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
437
+ let fill: string; let item: any; let renderText: string;
438
+ let opacity: number; let rectPath: string = '';
439
+ const secondaryEle: HTMLElement = document.getElementById(treeMap.element.id + '_Secondary_Element');
440
+ let groupId: string; let templateEle: HTMLElement; let gap: number;
441
+ let textStyle: FontModel; const levels: LevelSettings[] = treeMap.levels as LevelSettings[];
442
+ this.layoutGroup = this.renderer.createGroup({ id: elementID + '_TreeMap_' + mode + '_Layout' });
443
+ let itemGroup: Element; let template: string | Function; let border: BorderModel;
444
+ const templateGroup: HTMLElement = createElement('div', {
445
+ id: treeMap.element.id + '_Label_Template_Group',
446
+ className: 'template'
447
+ });
448
+ templateGroup.style.cssText = 'overflow: hidden; position: absolute;pointer-events: none;' +
449
+ 'top:' + treeMap.areaRect.y + 'px;' +
450
+ 'left:' + treeMap.areaRect.x + 'px;' +
451
+ 'height:' + treeMap.areaRect.height + 'px;' +
452
+ 'width:' + treeMap.areaRect.width + 'px;';
453
+ let isLeafItem: boolean = false; const leaf: LeafItemSettings = treeMap.leafItemSettings as LeafItemSettings;
454
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
455
+ let childItems: any[]; let connectorText: string;
456
+ for (let i: number = 0; i < this.renderItems.length; i++) {
457
+ item = this.renderItems[i as number];
458
+ index = item['groupIndex'];
459
+ if (this.treemap.drillDownView && isNullOrUndefined(this.treemap.currentLevel)
460
+ && index > 0 || this.treemap.drillDownView
461
+ && index > (this.treemap.currentLevel + 1)) {
462
+ continue;
463
+ }
464
+ rect = item['rect'];
465
+ isLeafItem = item['isLeafItem'];
466
+ groupId = elementID + '_Level_Index_' + index + '_Item_Index_' + i;
467
+ itemGroup = this.renderer.createGroup({ id: groupId + '_Group' });
468
+ gap = (isLeafItem ? leaf.gap : levels[index as number].groupGap) / 2;
469
+ const treemapItemRect: Rect = this.treemap.totalRect ? (treeMap.legendSettings.visible ? this.treemap.totalRect
470
+ : convertToContainer(this.treemap.totalRect)) : this.treemap.areaRect;
471
+ if (treeMap.layoutType === 'Squarified') {
472
+ rect.width = Math.abs(rect.x - rect.width) - gap;
473
+ rect.height = Math.abs(rect.y - rect.height) - gap;
474
+ }
475
+ if (treeMap.renderDirection === 'TopRightBottomLeft') {
476
+ rect.x = (treemapItemRect.x + treemapItemRect.width) - rect.width - Math.abs(treemapItemRect.x - rect.x);
477
+ } else if (treeMap.renderDirection === 'BottomLeftTopRight') {
478
+ rect.y = (treemapItemRect.y + treemapItemRect.height) - rect.height - Math.abs(treemapItemRect.y - rect.y);
479
+ } else if (treeMap.renderDirection === 'BottomRightTopLeft') {
480
+ rect.x = (treemapItemRect.x + treemapItemRect.width) - rect.width - Math.abs(treemapItemRect.x - rect.x);
481
+ rect.y = (treemapItemRect.y + treemapItemRect.height) - rect.height - Math.abs(treemapItemRect.y - rect.y);
482
+ }
483
+ getItemColor = this.getItemColor(isLeafItem, item);
484
+ fill = getItemColor['fill'];
485
+ opacity = getItemColor['opacity'];
486
+ format = isLeafItem ? leaf.labelFormat : (levels[index as number]).headerFormat;
487
+ let levelName: string;
488
+ txtVisible = isLeafItem ? leaf.showLabels : (levels[index as number]).showHeader;
489
+ if (index === this.treemap.currentLevel) {
490
+ if (this.treemap.enableBreadcrumb) {
491
+ const re: RegExp = /#/gi;
492
+ connectorText = '#' + this.treemap.breadcrumbConnector + '#';
493
+ levelName = item['levelOrderName'].replace(re, connectorText);
494
+ levelName = index !== 0 ? '#' + levelName : levelName;
495
+ } else {
496
+ levelName = item['name'];
497
+ }
498
+ } else {
499
+ if (this.treemap.enableBreadcrumb) {
500
+ item['isDrilled'] = false;
501
+ }
502
+ levelName = item['name'];
503
+ }
504
+ renderText = textFormatter(format, item['data'], this.treemap) || levelName || 'undefined';
505
+ childItems = findChildren(item)['values'];
506
+ renderText = !isLeafItem && childItems && childItems.length > 0 && this.treemap.enableDrillDown ?
507
+ !item['isDrilled'] ? treeMap.enableRtl ? renderText + ' [+]' : '[+] ' + renderText :
508
+ treeMap.enableRtl ? renderText + ' [-]' : '[-] ' + renderText : renderText;
509
+ if (treeMap.enableHtmlSanitizer) {
510
+ renderText = SanitizeHtmlHelper.sanitize(renderText);
511
+ }
512
+ let fontFamily: string = (isLeafItem ? leaf.labelStyle.fontFamily : levels[index as number].headerStyle.fontFamily);
513
+ fontFamily = fontFamily || this.treemap.themeStyle.labelFontFamily;
514
+ let size: string = (isLeafItem ? leaf.labelStyle.size : levels[index as number].headerStyle.size);
515
+ size = size || this.treemap.themeStyle.labelFontSize;
516
+ let fontWeight: string = (isLeafItem ? leaf.labelStyle.fontWeight : levels[index as number].headerStyle.fontWeight);
517
+ fontWeight = fontWeight || this.treemap.themeStyle.fontWeight;
518
+ const color: string = (isLeafItem ? leaf.labelStyle.color : levels[index as number].headerStyle.color);
519
+ const fontStyle: string = (isLeafItem ? leaf.labelStyle.fontStyle : levels[index as number].headerStyle.fontStyle);
520
+ const textStyleOpacity: number = (isLeafItem ? leaf.labelStyle.opacity : levels[index as number].headerStyle.opacity);
521
+ textStyle = {
522
+ fontFamily: fontFamily, size: size, fontWeight: fontWeight, color: color, fontStyle: fontStyle, opacity: textStyleOpacity
523
+ };
524
+ border = isLeafItem ? leaf.border : levels[index as number].border;
525
+ position = !isLeafItem ? (levels[index as number].headerAlignment) === 'Near' ? 'TopLeft' : (levels[index as number].headerAlignment) === 'Center' ?
526
+ 'TopCenter' : 'TopRight' : leaf.labelPosition;
527
+ templatePosition = isLeafItem ? leaf.templatePosition : levels[index as number].templatePosition;
528
+ template = isLeafItem ? leaf.labelTemplate : levels[index as number].headerTemplate;
529
+ item['options'] = { border: border, opacity: opacity, fill: fill };
530
+ eventArgs = {
531
+ cancel: false, name: itemRendering, treemap: this.treemap, text: renderText,
532
+ currentItem: item, RenderItems: this.renderItems, options: item['options'], textColor: textStyle.color
533
+ };
534
+ this.treemap.trigger(itemRendering, eventArgs, (observedArgs: IItemRenderingEventArgs) => {
535
+ if (!observedArgs.cancel) {
536
+ rectPath = ' M ' + rect.x + ' ' + rect.y + ' L ' + (rect.x + rect.width) + ' ' + rect.y +
537
+ ' L ' + (rect.x + rect.width) + ' ' + (rect.y + rect.height) + ' L ' + rect.x + ' ' + (rect.y + rect.height) + 'z';
538
+ pathOptions = new PathOption(groupId + '_RectPath', fill, border.width, border.color, opacity, null, rectPath);
539
+ const path: Element = this.renderer.drawPath(pathOptions);
540
+ itemGroup.appendChild(path);
541
+ if (txtVisible) {
542
+ if (eventArgs.text !== renderText) {
543
+ eventArgs.text = textFormatter(eventArgs.text, item['data'], this.treemap) || levelName;
544
+ }
545
+ textStyle.color = eventArgs.textColor ? eventArgs.textColor : textStyle.color;
546
+ this.renderItemText(
547
+ eventArgs.text.toString(), itemGroup, textStyle, rect, interSectAction, groupId, fill,
548
+ position as LabelPosition, connectorText
549
+ );
550
+ }
551
+ if (template) {
552
+ templateEle = this.renderTemplate(secondaryEle, groupId, rect, templatePosition, template, item, isLeafItem);
553
+ if (!isNullOrUndefined(templateEle)) {
554
+ templateGroup.appendChild(templateEle);
555
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
556
+ (this.treemap as any).renderReactTemplates();
557
+ }
558
+ }
559
+ itemGroup.setAttribute('aria-label', item['name']);
560
+ if ((this.treemap.enableDrillDown && !isLeafItem) || (this.treemap.selectionSettings.enable ||
561
+ this.treemap.highlightSettings.enable)) {
562
+ itemGroup.setAttribute('role', 'button');
563
+ itemGroup.setAttribute('tabindex', this.treemap.tabIndex.toString());
564
+ (itemGroup as HTMLElement).style.outline = 'none';
565
+ (itemGroup as HTMLElement).style.cursor = this.treemap.highlightSettings.enable && !this.treemap.selectionSettings.enable && (this.treemap.enableDrillDown && item['groupIndex'] === (this.treemap.levels.length - 1)) ? 'default' :
566
+ this.treemap.highlightSettings.enable && !this.treemap.selectionSettings.enable && !this.treemap.enableDrillDown ? 'default' : 'pointer';
567
+ } else {
568
+ itemGroup.setAttribute('role', 'region');
569
+ }
570
+ maintainSelection(this.treemap, itemGroup, 'treeMapSelection');
571
+ this.layoutGroup.appendChild(itemGroup);
572
+ }
573
+ });
574
+ }
575
+ if (templateGroup.childNodes.length > 0) {
576
+ secondaryEle.appendChild(templateGroup);
577
+ }
578
+ this.treemap.svgObject.appendChild(this.layoutGroup);
579
+ maintainSelection(this.treemap, this.layoutGroup, 'treeMapSelection');
580
+ }
581
+
582
+ private renderItemText(
583
+ text: string, parentElement: Element, textStyle: FontModel, rect: Rect, interSectAction: LabelAlignment,
584
+ groupId: string, fill: string, position: LabelPosition, connectorText: string
585
+ ): void {
586
+ const padding: number = 5; let textSize: Size;
587
+ let textCollection: string[] = []; let customText: string | string[];
588
+ const tspanText: string[] = []; let height: number = 0; let textName: string;
589
+ textCollection = ((text.indexOf('<br>')) !== -1) ? text.split('<br>') : null;
590
+ customText = this.labelInterSectAction(rect, text, textStyle, interSectAction);
591
+ textSize = measureText(textCollection && textCollection[0] || customText[0], textStyle);
592
+ if (this.treemap.enableRtl) {
593
+ const labelSize: Size = measureText(text, textStyle);
594
+ const drillSymbolCount: number = text.search('[+]') || text.search('[-]');
595
+ if (rect.width < labelSize.width && drillSymbolCount > 0) {
596
+ const label: string = text.substring(drillSymbolCount - 1, text.length);
597
+ const drillSymbol: string = '[+]';
598
+ const drillSymbolSize: Size = measureText(drillSymbol, textStyle);
599
+ customText['0'] = textTrim(rect.width - drillSymbolSize.width - padding, customText[0], textStyle) + label;
600
+ }
601
+
602
+ }
603
+ const textLocation: Location = findLabelLocation(rect, position, textSize, 'Text', this.treemap);
604
+ if (!isNullOrUndefined(textCollection)) {
605
+ const collection: string[] = [];
606
+ let texts: string = null;
607
+ const maxNumber: number[] = [];
608
+ for (let i: number = 0; i < textCollection.length; i++) {
609
+ texts = textTrim((rect.width - 5), textCollection[i as number], textStyle);
610
+ textSize = measureText(texts, textStyle);
611
+ height += textSize.height;
612
+ maxNumber.push(textSize.width);
613
+ collection.push(texts);
614
+ }
615
+ customText = collection;
616
+ textSize.width = Math.max.apply(null, maxNumber);
617
+ textSize.height = height;
618
+ }
619
+ if (interSectAction === 'WrapByWord' || interSectAction === 'Wrap' || interSectAction === 'Trim') {
620
+ for (let j: number = 0; j < customText.length; j++) {
621
+ textSize = measureText(customText[j as number], textStyle);
622
+ height += textSize.height;
623
+ if ((rect.height - padding) > height) {
624
+ tspanText.push(customText[j as number]);
625
+ }
626
+ }
627
+ if (interSectAction === 'Wrap' && customText.length !== tspanText.length && tspanText.length) {
628
+ const collectionLength: number = tspanText.length - 1;
629
+ let stringText: string = tspanText[collectionLength as number];
630
+ stringText = stringText.substring(0, (stringText.length - 1)) + '...';
631
+ tspanText.splice(collectionLength);
632
+ if (stringText !== '...') {
633
+ tspanText.push(stringText);
634
+ }
635
+ }
636
+ } else {
637
+ textName = customText as string;
638
+ tspanText.push(textName);
639
+ }
640
+ const textOptions: TextOption = new TextOption(
641
+ groupId + '_Text', textLocation.x, textLocation.y, 'start', tspanText, '', '', connectorText
642
+ );
643
+ renderTextElement(textOptions, textStyle, textStyle.color || this.getSaturatedColor(fill), parentElement);
644
+ }
645
+
646
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
647
+ private getItemColor(isLeafItem: boolean, item: any): any {
648
+ const treemap: TreeMap = this.treemap;
649
+ let itemFill: string = isLeafItem ? treemap.leafItemSettings.fill : treemap.levels[item['groupIndex']].fill;
650
+ let itemOpacity: number = isLeafItem ? treemap.leafItemSettings.opacity : treemap.levels[item['groupIndex']].opacity;
651
+ if (!isNullOrUndefined(treemap.treemapLevelData.defaultLevelsData)) {
652
+ if (treemap.treemapLevelData.defaultLevelsData.length > 0) {
653
+ treemap.treemapLevelData.levelsData = treemap.treemapLevelData.defaultLevelsData;
654
+ }
655
+ }
656
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
657
+ const parentData: any[] = findChildren(treemap.treemapLevelData.levelsData[0])['values'];
658
+ const colorMapping: ColorMappingModel[] = isLeafItem ? treemap.leafItemSettings.colorMapping :
659
+ treemap.levels[item['groupIndex']].colorMapping;
660
+ if (colorMapping.length > 0) {
661
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
662
+ const option: any = colorMap(
663
+ colorMapping, item['data'][treemap.equalColorValuePath],
664
+ item['data'][treemap.rangeColorValuePath]);
665
+ if (!isNullOrUndefined(option)) {
666
+ itemFill = !isNullOrUndefined(option['fill']) ? option['fill'] : treemap.leafItemSettings.fill;
667
+ itemOpacity = option['opacity'];
668
+ }
669
+ } else {
670
+ for (let i: number = 0; i < parentData.length; i++) {
671
+ if ((parentData[i as number]['levelOrderName'] as string) === (item['levelOrderName'] as string).split('#')[0]) {
672
+ itemFill = !isNullOrUndefined(itemFill) ? itemFill : !isNullOrUndefined(treemap.colorValuePath) ?
673
+ parentData[i as number]['data'][treemap.colorValuePath] : !isNullOrUndefined(item['options']) ?
674
+ item['options'].fill : (!isNullOrUndefined(treemap.palette) && treemap.palette.length > 0) ?
675
+ treemap.palette[i % treemap.palette.length] : '#808080';
676
+ }
677
+ }
678
+ }
679
+ return { fill: itemFill, opacity: itemOpacity };
680
+ }
681
+
682
+ /**
683
+ * To find saturated color for datalabel
684
+ *
685
+ * @param {string} color - Specifies the color
686
+ * @returns {string} - Returns the color
687
+ */
688
+ private getSaturatedColor(color: string): string {
689
+ let saturatedColor: string = color;
690
+ saturatedColor = (saturatedColor === 'transparent') ? window.getComputedStyle(document.body, null).backgroundColor : saturatedColor;
691
+ const rgbValue: ColorValue = convertHexToColor(colorNameToHex(saturatedColor));
692
+ const contrast: number = Math.round((rgbValue.r * 299 + rgbValue.g * 587 + rgbValue.b * 114) / 1000);
693
+ return contrast >= 128 ? 'black' : 'white';
694
+ }
695
+
696
+ private renderTemplate(
697
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, max-len
698
+ secondaryEle: HTMLElement, groupId: string, rect: Rect, position: LabelPosition, template: string | Function, item: any, isLeafItem: boolean
699
+ ): HTMLElement {
700
+ const templateId: string = isLeafItem ? groupId + '_LabelTemplate' : groupId + '_HeaderTemplate';
701
+ const baseTemplateId: string = isLeafItem ? '_LabelTemplate' : '_HeaderTemplate';
702
+ if (isNullOrUndefined(template['prototype']) && typeof template === 'string') {
703
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
704
+ const keys: any[] = Object.keys(item['data']);
705
+ for (let i: number = 0; i < keys.length; i++) {
706
+ const regExp: RegExpConstructor = RegExp;
707
+ template = template.replace(new regExp('{{:' + <string>keys[i as number] + '}}', 'g'), item['data'][keys[i as number].toString()]);
708
+ }
709
+ }
710
+ if (this.treemap.enableHtmlSanitizer && typeof template === 'string') {
711
+ template = SanitizeHtmlHelper.sanitize(template);
712
+ }
713
+ let labelElement: HTMLElement;
714
+ if (!isNullOrUndefined(document.getElementById(this.treemap.element.id + '_Secondary_Element'))) {
715
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
716
+ const templateFn: any = getTemplateFunction(template);
717
+ const templateElement: HTMLCollection = templateFn(item['data'], this.treemap, template, this.treemap.element.id + baseTemplateId, false);
718
+ labelElement = convertElement(templateElement, templateId, item['data']);
719
+ const templateSize: Size = measureElement(labelElement, secondaryEle);
720
+ const templateLocation: Location = findLabelLocation(rect, position, templateSize, 'Template', this.treemap);
721
+ labelElement.style.left = templateLocation.x + 'px';
722
+ labelElement.style.top = templateLocation.y + 'px';
723
+ }
724
+ return labelElement;
725
+ }
726
+ private labelInterSectAction(rect: Rect, text: string, textStyle: FontModel, alignment: LabelAlignment): string | string[] {
727
+ let textValue: string | string[]; const maxWidth: number = rect.width - 10;
728
+ switch (alignment) {
729
+ case 'Hide':
730
+ textValue = [hide(maxWidth, rect.height, text, textStyle)];
731
+ break;
732
+ case 'Trim':
733
+ textValue = [textTrim((maxWidth + 3), text, textStyle)];
734
+ break;
735
+ case 'WrapByWord':
736
+ textValue = wordWrap(maxWidth, text, textStyle);
737
+ break;
738
+ case 'Wrap':
739
+ textValue = textWrap(maxWidth, text, textStyle);
740
+ break;
741
+ }
742
+ return textValue;
743
+ }
744
+
745
+ /**
746
+ *
747
+ * @returns {void}
748
+ * @private
749
+ */
750
+ public destroy(): void {
751
+ this.treemap = null;
752
+ this.currentRect = null;
753
+ this.layoutGroup = null;
754
+ this.renderer = null;
755
+ this.renderItems = [];
756
+ this.parentData = [];
757
+ }
758
+ }