@syncfusion/ej2-treemap 19.2.55 → 19.4.38

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