stockchart-treemap 1.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.
package/README.md ADDED
@@ -0,0 +1,90 @@
1
+ # stockchart-treemap
2
+
3
+ Готовый к публикации на npm пакет с самостоятельным TreeMap-компонентом для Angular.
4
+ Компонент работает как standalone, поддерживает подсветку по `TreeMapColorScale`,
5
+ источники данных в виде массива, Observable или лоадера, а также кастомные шаблоны плиток и заголовков.
6
+
7
+ ## Сборка и публикация
8
+ 1. Соберите библиотеку:
9
+ ```bash
10
+ npm run ng -- build stockchart-treemap
11
+ ```
12
+ 2. Перейдите в папку сборки и опубликуйте пакет (убедитесь, что вошли в npm):
13
+ ```bash
14
+ cd dist/stockchart-treemap
15
+ npm publish --access public
16
+ ```
17
+
18
+ ## Сборка примерных компонентов для отладки
19
+ Для локального тестирования и пошаговой отладки демо-компонентов соберите отдельный пакет `stockchart-treemap-examples`:
20
+ ```bash
21
+ npm run build:stockchart-treemap-examples
22
+ ```
23
+ Сборка использует tsconfig для примеров и кладёт артефакты в `dist/stockchart-treemap-examples`, чтобы их можно было подключить в песочнице или во внешнем приложении без вмешательства в основной пакет.
24
+
25
+ Для живого просмотра примеров запустите дев-сервер с подходящей конфигурацией:
26
+ ```bash
27
+ # карта рынка на стандартном порту 4200
28
+ ng serve stockchart-treemap-examples
29
+
30
+ # вариант с собственными цветами на порту 4200
31
+ ng serve stockchart-treemap-examples --configuration self-colored
32
+ ```
33
+
34
+ ## Подключение
35
+ ```ts
36
+ import { bootstrapApplication } from '@angular/platform-browser';
37
+ import { AppComponent } from './app/app.component';
38
+ import { TreeMapComponent } from 'stockchart-treemap';
39
+
40
+ bootstrapApplication(AppComponent, {
41
+ providers: [
42
+ // TreeMapComponent подключается как standalone в импортах компонентов
43
+ ]
44
+ });
45
+ ```
46
+
47
+ В компоненте-доме добавьте `TreeMapComponent` или готовые демо-обёртки в секцию `imports` и используйте селектор
48
+ `<stockchart-treemap>` прямо в шаблоне.
49
+
50
+ ## Пример «app-stockchart-treemap» с сгенерированными данными
51
+ ```ts
52
+ import { AppStockchartTreemapExampleComponent, createMockMarketMap } from 'stockchart-treemap';
53
+
54
+ @Component({
55
+ standalone: true,
56
+ imports: [AppStockchartTreemapExampleComponent]
57
+ })
58
+ export class DemoHost {}
59
+ ```
60
+
61
+ Компонент сам генерирует дерево с секторами и тикерами, выставляет `colorValueField: 'change'` и
62
+ использует `TreeMapColorScale` для плавной подсветки приростов и падений.
63
+
64
+ ## Базовое использование с авторасчётом цветов
65
+ ```html
66
+ <stockchart-treemap
67
+ [data]="data"
68
+ [options]="{ textField: 'name', valueField: 'value', colorValueField: 'change', colorScale: performanceColorScale }">
69
+ </stockchart-treemap>
70
+ ```
71
+ ```ts
72
+ import { performanceColorScale, createPerformanceTreemap } from 'stockchart-treemap';
73
+
74
+ @Component({
75
+ standalone: true,
76
+ imports: [TreeMapComponent]
77
+ })
78
+ export class SimpleDemo {
79
+ data = createPerformanceTreemap();
80
+ options = {
81
+ textField: 'name',
82
+ valueField: 'value',
83
+ colorValueField: 'change',
84
+ colorScale: performanceColorScale,
85
+ colors: []
86
+ };
87
+ }
88
+ ```
89
+
90
+ Плитки не содержат поля `color`, поэтому библиотека самостоятельно рассчитывает оттенки на основе значений из `change`.
@@ -0,0 +1,755 @@
1
+ import * as i0 from '@angular/core';
2
+ import { EventEmitter, Output, Input, ViewChild, ChangeDetectionStrategy, Component } from '@angular/core';
3
+ import * as i1 from '@angular/common';
4
+ import { CommonModule } from '@angular/common';
5
+ import { defer, isObservable, from, of, timer } from 'rxjs';
6
+ import { switchMap } from 'rxjs/operators';
7
+
8
+ const UNDEFINED = 'undefined';
9
+ function getField(path, row) {
10
+ if (row === null || row === undefined)
11
+ return row;
12
+ const parts = String(path ?? '').split('.').filter(Boolean);
13
+ let cur = row;
14
+ for (const p of parts) {
15
+ if (cur === null || cur === undefined)
16
+ return undefined;
17
+ cur = cur[p];
18
+ }
19
+ return cur;
20
+ }
21
+ function defined(value) {
22
+ return typeof value !== UNDEFINED;
23
+ }
24
+ function toNumber(v) {
25
+ if (v === null || v === undefined)
26
+ return 0;
27
+ const n = typeof v === 'number' ? v : parseFloat(String(v).replace(',', '.'));
28
+ return Number.isFinite(n) ? n : 0;
29
+ }
30
+ function totalAreaOf(items) {
31
+ let total = 0;
32
+ for (let i = 0; i < items.length; i++)
33
+ total += (items[i].area ?? 0);
34
+ return total;
35
+ }
36
+ function round(value) {
37
+ const power = Math.pow(10, 4);
38
+ return Math.round(value * power) / power;
39
+ }
40
+ function roundN(value, decimals) {
41
+ const power = Math.pow(10, decimals);
42
+ return Math.round(value * power) / power;
43
+ }
44
+ function colorsByLength(min, max, length) {
45
+ const minRGBtoDecimal = rgbToDecimal(min);
46
+ const maxRGBtoDecimal = rgbToDecimal(max);
47
+ const isDarker = colorBrightness(min) - colorBrightness(max) < 0;
48
+ const colors = [];
49
+ colors.push(min);
50
+ for (let i = 0; i < length; i++) {
51
+ const rgbColor = {
52
+ r: colorByIndex(minRGBtoDecimal.r, maxRGBtoDecimal.r, i, length, isDarker),
53
+ g: colorByIndex(minRGBtoDecimal.g, maxRGBtoDecimal.g, i, length, isDarker),
54
+ b: colorByIndex(minRGBtoDecimal.b, maxRGBtoDecimal.b, i, length, isDarker)
55
+ };
56
+ colors.push(buildColorFromRGB(rgbColor));
57
+ }
58
+ colors.push(max);
59
+ return colors;
60
+ }
61
+ function projectColorByValue(value, min, max, scale) {
62
+ const hasCenter = typeof scale.center === 'string';
63
+ const clampedMin = Math.min(min, max);
64
+ const clampedMax = Math.max(min, max);
65
+ if (clampedMin === clampedMax) {
66
+ return scale.center ?? scale.max ?? scale.min;
67
+ }
68
+ if (hasCenter && clampedMin < 0 && clampedMax > 0) {
69
+ if (value >= 0) {
70
+ const ratio = clampedMax === 0 ? 0 : clamp01(value / clampedMax);
71
+ return interpolateColor(scale.center ?? scale.min, scale.max, ratio);
72
+ }
73
+ const ratio = clampedMin === 0 ? 0 : clamp01(value / clampedMin);
74
+ return interpolateColor(scale.center ?? scale.max, scale.min, ratio);
75
+ }
76
+ const ratio = clamp01((value - clampedMin) / (clampedMax - clampedMin));
77
+ return interpolateColor(scale.min, scale.max, ratio);
78
+ }
79
+ function colorByIndex(min, max, index, length, isDarker) {
80
+ const minColor = Math.min(Math.abs(min), Math.abs(max));
81
+ const maxColor = Math.max(Math.abs(min), Math.abs(max));
82
+ const step = (maxColor - minColor) / (length + 1);
83
+ const currentStep = step * (index + 1);
84
+ return isDarker ? (minColor + currentStep) : (maxColor - currentStep);
85
+ }
86
+ function interpolateColor(min, max, ratio) {
87
+ const start = rgbToDecimal(min);
88
+ const end = rgbToDecimal(max);
89
+ const t = clamp01(ratio);
90
+ return buildColorFromRGB({
91
+ r: lerp(start.r, end.r, t),
92
+ g: lerp(start.g, end.g, t),
93
+ b: lerp(start.b, end.b, t)
94
+ });
95
+ }
96
+ function buildColorFromRGB(color) {
97
+ return '#' + decimalToRgb(color.r) + decimalToRgb(color.g) + decimalToRgb(color.b);
98
+ }
99
+ function lerp(from, to, t) {
100
+ return from + (to - from) * t;
101
+ }
102
+ function rgbToDecimal(color) {
103
+ color = color.replace('#', '');
104
+ const rgbColor = colorToRGB(color);
105
+ return {
106
+ r: rgbToHex(rgbColor.r),
107
+ g: rgbToHex(rgbColor.g),
108
+ b: rgbToHex(rgbColor.b)
109
+ };
110
+ }
111
+ function decimalToRgb(number) {
112
+ let result = Math.round(number).toString(16).toUpperCase();
113
+ if (result.length === 1)
114
+ result = '0' + result;
115
+ return result;
116
+ }
117
+ function colorToRGB(color) {
118
+ const colorLength = color.length;
119
+ const rgbColor = {};
120
+ if (colorLength === 3) {
121
+ rgbColor.r = color[0];
122
+ rgbColor.g = color[1];
123
+ rgbColor.b = color[2];
124
+ }
125
+ else {
126
+ rgbColor.r = color.substring(0, 2);
127
+ rgbColor.g = color.substring(2, 4);
128
+ rgbColor.b = color.substring(4, 6);
129
+ }
130
+ return rgbColor;
131
+ }
132
+ function rgbToHex(rgb) {
133
+ return parseInt(rgb.toString(16), 16);
134
+ }
135
+ function colorBrightness(color) {
136
+ let brightness = 0;
137
+ if (color) {
138
+ const c = rgbToDecimal(color);
139
+ brightness = Math.sqrt(0.241 * c.r * c.r + 0.691 * c.g * c.g + 0.068 * c.b * c.b);
140
+ }
141
+ return brightness;
142
+ }
143
+ function clamp01(value) {
144
+ if (!Number.isFinite(value))
145
+ return 0;
146
+ return Math.min(1, Math.max(0, value));
147
+ }
148
+
149
+ const MAX_VALUE = Number.MAX_VALUE;
150
+ class SquarifiedLayout {
151
+ constructor() {
152
+ this.orientation = 'h';
153
+ }
154
+ compute(children, coord) {
155
+ if (!(coord.width >= coord.height && this.layoutHorizontal())) {
156
+ this.layoutChange();
157
+ }
158
+ if (children.length > 0)
159
+ this.layoutChildren(children, coord);
160
+ }
161
+ layoutChildren(items, coord) {
162
+ const parentArea = coord.width * coord.height;
163
+ let totalArea = 0;
164
+ const itemsArea = [];
165
+ for (let i = 0; i < items.length; i++) {
166
+ itemsArea[i] = parseFloat(String(items[i].value ?? 0));
167
+ totalArea += itemsArea[i];
168
+ }
169
+ if (totalArea <= 0)
170
+ return;
171
+ for (let i = 0; i < itemsArea.length; i++) {
172
+ items[i].area = parentArea * itemsArea[i] / totalArea;
173
+ }
174
+ const minimumSideValue = this.layoutHorizontal() ? coord.height : coord.width;
175
+ const firstElement = [items[0]];
176
+ const tail = items.slice(1);
177
+ this.squarify(tail, firstElement, minimumSideValue, coord);
178
+ }
179
+ squarify(tail, initElement, width, coord) {
180
+ this.computeDim(tail, initElement, width, coord);
181
+ }
182
+ computeDim(tail, initElement, width, coord) {
183
+ if (tail.length + initElement.length === 1) {
184
+ const element = tail.length === 1 ? tail : initElement;
185
+ this.layoutLast(element, coord);
186
+ return;
187
+ }
188
+ if (tail.length >= 2 && initElement.length === 0) {
189
+ initElement = [tail[0]];
190
+ tail = tail.slice(1);
191
+ }
192
+ if (tail.length === 0) {
193
+ if (initElement.length > 0)
194
+ this.layoutRow(initElement, width, coord);
195
+ return;
196
+ }
197
+ const firstElement = tail[0];
198
+ if (this.worstAspectRatio(initElement, width) >= this.worstAspectRatio([firstElement, ...initElement], width)) {
199
+ this.computeDim(tail.slice(1), [...initElement, firstElement], width, coord);
200
+ }
201
+ else {
202
+ const newCoords = this.layoutRow(initElement, width, coord);
203
+ this.computeDim(tail, [], newCoords.dim, newCoords);
204
+ }
205
+ }
206
+ layoutLast(items, coord) {
207
+ items[0].coord = coord;
208
+ }
209
+ layoutRow(items, width, coord) {
210
+ return this.layoutHorizontal() ? this.layoutV(items, width, coord) : this.layoutH(items, width, coord);
211
+ }
212
+ layoutVertical() { return this.orientation === 'v'; }
213
+ layoutHorizontal() { return this.orientation === 'h'; }
214
+ layoutChange() {
215
+ this.orientation = this.layoutVertical() ? 'h' : 'v';
216
+ }
217
+ worstAspectRatio(items, width) {
218
+ if (!items || items.length === 0)
219
+ return MAX_VALUE;
220
+ let areaSum = 0;
221
+ let maxArea = 0;
222
+ let minArea = MAX_VALUE;
223
+ for (let i = 0; i < items.length; i++) {
224
+ const area = items[i].area ?? 0;
225
+ areaSum += area;
226
+ minArea = (minArea < area) ? minArea : area;
227
+ maxArea = (maxArea > area) ? maxArea : area;
228
+ }
229
+ if (areaSum <= 0)
230
+ return MAX_VALUE;
231
+ return Math.max((width * width * maxArea) / (areaSum * areaSum), (areaSum * areaSum) / (width * width * minArea));
232
+ }
233
+ layoutV(items, width, coord) {
234
+ const totalArea = totalAreaOf(items);
235
+ let top = 0;
236
+ width = round(totalArea / width);
237
+ for (let i = 0; i < items.length; i++) {
238
+ const height = round((items[i].area ?? 0) / width);
239
+ items[i].coord = {
240
+ height,
241
+ width,
242
+ top: coord.top + top,
243
+ left: coord.left
244
+ };
245
+ top += height;
246
+ }
247
+ const ans = {
248
+ height: coord.height,
249
+ width: coord.width - width,
250
+ top: coord.top,
251
+ left: coord.left + width,
252
+ dim: 0
253
+ };
254
+ ans.dim = Math.min(ans.width, ans.height);
255
+ if (ans.dim !== ans.height)
256
+ this.layoutChange();
257
+ return ans;
258
+ }
259
+ layoutH(items, width, coord) {
260
+ const totalArea = totalAreaOf(items);
261
+ const height = round(totalArea / width);
262
+ const top = coord.top;
263
+ let left = 0;
264
+ for (let i = 0; i < items.length; i++) {
265
+ items[i].coord = {
266
+ height,
267
+ width: round((items[i].area ?? 0) / height),
268
+ top,
269
+ left: coord.left + left
270
+ };
271
+ left += items[i].coord.width;
272
+ }
273
+ const ans = {
274
+ height: coord.height - height,
275
+ width: coord.width,
276
+ top: coord.top + height,
277
+ left: coord.left,
278
+ dim: 0
279
+ };
280
+ ans.dim = Math.min(ans.width, ans.height);
281
+ if (ans.dim !== ans.width)
282
+ this.layoutChange();
283
+ return ans;
284
+ }
285
+ }
286
+ class SliceAndDiceLayout {
287
+ constructor(vertical) {
288
+ this.vertical = vertical;
289
+ this.quotient = vertical ? 1 : 0;
290
+ }
291
+ compute(children, coord) {
292
+ if (children.length === 0)
293
+ return;
294
+ const parentArea = coord.width * coord.height;
295
+ let totalArea = 0;
296
+ const itemsArea = [];
297
+ for (let i = 0; i < children.length; i++) {
298
+ itemsArea[i] = parseFloat(String(children[i].value ?? 0));
299
+ totalArea += itemsArea[i];
300
+ children[i].vertical = this.vertical;
301
+ }
302
+ if (totalArea <= 0)
303
+ return;
304
+ for (let i = 0; i < itemsArea.length; i++) {
305
+ children[i].area = parentArea * itemsArea[i] / totalArea;
306
+ }
307
+ this.sliceAndDice(children, coord);
308
+ }
309
+ sliceAndDice(items, coord) {
310
+ const totalArea = totalAreaOf(items);
311
+ if ((items[0].level % 2) === this.quotient) {
312
+ this.layoutHorizontal(items, coord, totalArea);
313
+ }
314
+ else {
315
+ this.layoutVertical(items, coord, totalArea);
316
+ }
317
+ }
318
+ layoutHorizontal(items, coord, totalArea) {
319
+ let left = 0;
320
+ for (let i = 0; i < items.length; i++) {
321
+ const item = items[i];
322
+ const width = (item.area ?? 0) / (totalArea / coord.width);
323
+ item.coord = {
324
+ height: coord.height,
325
+ width,
326
+ top: coord.top,
327
+ left: coord.left + left
328
+ };
329
+ left += width;
330
+ }
331
+ }
332
+ layoutVertical(items, coord, totalArea) {
333
+ let top = 0;
334
+ for (let i = 0; i < items.length; i++) {
335
+ const item = items[i];
336
+ const height = (item.area ?? 0) / (totalArea / coord.height);
337
+ item.coord = {
338
+ height,
339
+ width: coord.width,
340
+ top: coord.top + top,
341
+ left: coord.left
342
+ };
343
+ top += height;
344
+ }
345
+ }
346
+ }
347
+
348
+ class TreeMapComponent {
349
+ get cfg() {
350
+ const valueField = this.options.valueField ?? 'value';
351
+ const colorValueField = this.options.colorValueField ?? valueField;
352
+ return {
353
+ type: 'squarified',
354
+ textField: 'name',
355
+ valueField,
356
+ colorField: 'color',
357
+ colorValueField,
358
+ childrenField: 'items',
359
+ colors: ['#5B8FF9', '#5AD8A6', '#5D7092', '#F6BD16', '#E8684A', '#6DC8EC'],
360
+ colorScale: undefined,
361
+ titleSize: 26,
362
+ showTopLevelTitles: true,
363
+ deriveParentValueFromChildren: true,
364
+ roundDecimals: 4,
365
+ minTileSize: 2,
366
+ ...this.options
367
+ };
368
+ }
369
+ constructor(zone, cdr) {
370
+ this.zone = zone;
371
+ this.cdr = cdr;
372
+ // 1) Прямые данные
373
+ this.data = null;
374
+ this.refreshMs = null; // например 5000. null/0 = без polling
375
+ this.options = {};
376
+ this.dataBound = new EventEmitter();
377
+ this.tileClick = new EventEmitter();
378
+ this.tileHover = new EventEmitter();
379
+ this.root = null;
380
+ this.hoveredUid = null;
381
+ this.ro = null;
382
+ this.rebuildQueued = false;
383
+ this.uidSeq = 0;
384
+ this.colorIdx = 0;
385
+ this.sourceSub = null;
386
+ this.trackByUid = (_, n) => n.uid;
387
+ }
388
+ ngAfterViewInit() {
389
+ this.zone.runOutsideAngular(() => {
390
+ this.ro = new ResizeObserver(() => this.queueRebuild());
391
+ this.ro.observe(this.host.nativeElement);
392
+ });
393
+ this.resetDataSource();
394
+ this.rebuild(); // на случай, если data уже есть
395
+ }
396
+ ngOnChanges(changes) {
397
+ if (changes['data$'] || changes['loader'] || changes['refreshMs']) {
398
+ this.resetDataSource();
399
+ }
400
+ if (changes['data'] || changes['options']) {
401
+ this.queueRebuild();
402
+ }
403
+ }
404
+ ngOnDestroy() {
405
+ this.ro?.disconnect();
406
+ this.ro = null;
407
+ this.sourceSub?.unsubscribe();
408
+ this.sourceSub = null;
409
+ }
410
+ // ===== Template helpers =====
411
+ isLeaf(node) {
412
+ return !(node.children && node.children.length);
413
+ }
414
+ isInverse(node) {
415
+ return colorBrightness(node.color) > 180;
416
+ }
417
+ isTitleSide() {
418
+ return this.cfg.type === 'horizontal';
419
+ }
420
+ titleSizeFor(node) {
421
+ // Псевдо-корень (level 0, dataItem=null) не имеет заголовка
422
+ if (!node.dataItem)
423
+ return 0;
424
+ const hasChildren = !!node.children?.length;
425
+ if (!hasChildren)
426
+ return 0;
427
+ // top-level показываем/скрываем опцией
428
+ if (node.level === 1 && !this.cfg.showTopLevelTitles)
429
+ return 0;
430
+ const hasText = (node.text ?? '').trim().length > 0;
431
+ const hasTpl = !!this.titleTemplate;
432
+ if (!hasText && !hasTpl)
433
+ return 0;
434
+ return Math.max(0, this.cfg.titleSize);
435
+ }
436
+ onTileMouseEnter(node) {
437
+ if (!node.dataItem)
438
+ return;
439
+ this.hoveredUid = node.uid;
440
+ this.tileHover.emit({ node, dataItem: node.dataItem, path: this.buildPath(node) });
441
+ }
442
+ onTileMouseLeave(node) {
443
+ if (this.hoveredUid === node.uid)
444
+ this.hoveredUid = null;
445
+ }
446
+ onTileClick(node) {
447
+ if (!node.dataItem)
448
+ return;
449
+ this.tileClick.emit({ node, dataItem: node.dataItem, path: this.buildPath(node) });
450
+ }
451
+ /** Можно дергать из родителя: this.treemap.refreshNow() */
452
+ refreshNow() {
453
+ // если источник data$ — просто rebuild
454
+ // если loader — принудительно дернем еще раз
455
+ if (this.loader && !this.data$) {
456
+ this.resetDataSource();
457
+ return;
458
+ }
459
+ this.queueRebuild();
460
+ }
461
+ // ===== Data source wiring =====
462
+ resetDataSource() {
463
+ this.sourceSub?.unsubscribe();
464
+ this.sourceSub = null;
465
+ // Приоритет: data$ > loader > data
466
+ if (this.data$) {
467
+ this.sourceSub = this.data$.subscribe(arr => {
468
+ this.data = arr ?? [];
469
+ this.queueRebuild();
470
+ });
471
+ return;
472
+ }
473
+ if (this.loader) {
474
+ const oneLoad$ = defer(() => {
475
+ const res = this.loader();
476
+ if (isObservable(res))
477
+ return res;
478
+ if (res instanceof Promise)
479
+ return from(res);
480
+ return of(res);
481
+ });
482
+ const poll = this.refreshMs && this.refreshMs > 0;
483
+ const stream$ = poll
484
+ ? timer(0, this.refreshMs).pipe(switchMap(() => oneLoad$))
485
+ : oneLoad$;
486
+ this.sourceSub = stream$.subscribe(arr => {
487
+ this.data = arr ?? [];
488
+ this.cdr.markForCheck();
489
+ this.queueRebuild();
490
+ });
491
+ }
492
+ }
493
+ // ===== Layout pipeline =====
494
+ queueRebuild() {
495
+ if (this.rebuildQueued)
496
+ return;
497
+ this.rebuildQueued = true;
498
+ queueMicrotask(() => {
499
+ this.rebuildQueued = false;
500
+ this.rebuild();
501
+ });
502
+ }
503
+ rebuild() {
504
+ const el = this.host?.nativeElement;
505
+ if (!el)
506
+ return;
507
+ const rect = el.getBoundingClientRect();
508
+ const width = Math.max(0, rect.width);
509
+ const height = Math.max(0, rect.height);
510
+ if (width === 0 || height === 0)
511
+ return;
512
+ this.uidSeq = 0;
513
+ this.colorIdx = 0;
514
+ const root = {
515
+ uid: this.nextUid(),
516
+ level: 0,
517
+ text: '',
518
+ value: 0,
519
+ coord: { width, height, top: 0, left: 0 },
520
+ dataItem: null,
521
+ children: this.buildNodes(this.data ?? [], 1)
522
+ };
523
+ this.sortTree(root);
524
+ if (this.cfg.deriveParentValueFromChildren) {
525
+ this.deriveValues(root);
526
+ }
527
+ this.applyColors(root);
528
+ const layout = this.cfg.type === 'vertical' ? new SliceAndDiceLayout(true) :
529
+ this.cfg.type === 'horizontal' ? new SliceAndDiceLayout(false) :
530
+ new SquarifiedLayout();
531
+ this.layoutNode(root, layout);
532
+ // округление координат (как round в исходнике)
533
+ const d = this.cfg.roundDecimals;
534
+ this.roundCoordsDeep(root, d);
535
+ this.root = root;
536
+ this.cdr.markForCheck(); // <-- важно
537
+ this.zone.run(() => this.dataBound.emit());
538
+ }
539
+ buildNodes(items, level) {
540
+ const { textField, valueField, colorField, colorValueField, childrenField } = this.cfg;
541
+ const nodes = [];
542
+ for (const item of items ?? []) {
543
+ const rawText = getField(textField, item);
544
+ const rawValue = getField(valueField, item);
545
+ const rawColor = getField(colorField, item);
546
+ const rawColorValue = getField(colorValueField, item);
547
+ const rawChildren = getField(childrenField, item);
548
+ const childrenArr = Array.isArray(rawChildren) ? rawChildren : [];
549
+ const node = {
550
+ uid: this.nextUid(),
551
+ level,
552
+ text: (rawText ?? '') + '',
553
+ value: toNumber(rawValue),
554
+ colorValue: toNumber(rawColorValue),
555
+ color: typeof rawColor === 'string' ? rawColor : undefined,
556
+ coord: { width: 0, height: 0, top: 0, left: 0 },
557
+ dataItem: item,
558
+ children: childrenArr.length ? this.buildNodes(childrenArr, level + 1) : undefined
559
+ };
560
+ // Пропускаем только "пустые" листья без детей и нулевой value; контейнеры с children оставляем,
561
+ // чтобы значение пересчиталось от потомков и дерево не опустело (случай value=0 у корня данных).
562
+ if (Number.isFinite(node.value) && node.value === 0 && !childrenArr.length)
563
+ continue;
564
+ nodes.push(node);
565
+ }
566
+ return nodes;
567
+ }
568
+ sortTree(node) {
569
+ if (!node.children?.length)
570
+ return;
571
+ node.children.sort((a, b) => (b.value ?? 0) - (a.value ?? 0));
572
+ for (const c of node.children)
573
+ this.sortTree(c);
574
+ }
575
+ deriveValues(node) {
576
+ if (!node.children?.length) {
577
+ return Number.isFinite(node.value) ? node.value : 0;
578
+ }
579
+ const sum = node.children.reduce((acc, c) => acc + this.deriveValues(c), 0);
580
+ if (!Number.isFinite(node.value) || node.value === 0)
581
+ node.value = sum;
582
+ return node.value;
583
+ }
584
+ applyColors(node) {
585
+ const items = node.children;
586
+ if (!items?.length)
587
+ return;
588
+ if (this.cfg.colorScale) {
589
+ this.applyProjectedColors(items, this.cfg.colorScale);
590
+ }
591
+ const colors = this.cfg.colors;
592
+ if (colors.length) {
593
+ this.applyPaletteColors(items, colors);
594
+ }
595
+ else {
596
+ this.applyFallbackColors(items);
597
+ }
598
+ for (const c of items)
599
+ this.applyColors(c);
600
+ }
601
+ applyProjectedColors(nodes, scale) {
602
+ const values = nodes.flatMap(n => this.collectValues(n));
603
+ if (!values.length)
604
+ return;
605
+ const min = Math.min(...values);
606
+ const max = Math.max(...values);
607
+ const allNodes = nodes.flatMap(n => this.collectNodes(n));
608
+ for (const n of allNodes) {
609
+ if (!defined(n.color)) {
610
+ const colorValue = defined(n.colorValue) ? n.colorValue : n.value ?? 0;
611
+ n.color = projectColorByValue(colorValue, min, max, scale);
612
+ }
613
+ }
614
+ }
615
+ applyPaletteColors(nodes, colors) {
616
+ if (!nodes.length)
617
+ return;
618
+ const chosen = colors[this.colorIdx % colors.length];
619
+ let colorRange = null;
620
+ // ВАЖНО: намеренно повторяем логику из твоего кода:
621
+ // colorsByLength возвращает length+2, а мы берём первые length элементов.
622
+ if (Array.isArray(chosen)) {
623
+ colorRange = colorsByLength(chosen[0], chosen[1], nodes.length);
624
+ }
625
+ let leafNodes = false;
626
+ for (let i = 0; i < nodes.length; i++) {
627
+ const node = nodes[i];
628
+ if (!defined(node.color)) {
629
+ node.color = colorRange ? colorRange[i] : chosen;
630
+ }
631
+ if (!node.children?.length)
632
+ leafNodes = true;
633
+ }
634
+ if (leafNodes)
635
+ this.colorIdx++;
636
+ }
637
+ applyFallbackColors(nodes) {
638
+ for (const n of nodes) {
639
+ if (!defined(n.color))
640
+ n.color = '#B0B0B0';
641
+ }
642
+ }
643
+ collectNodes(node) {
644
+ const res = [node];
645
+ for (const c of node.children ?? [])
646
+ res.push(...this.collectNodes(c));
647
+ return res;
648
+ }
649
+ collectValues(node) {
650
+ const value = defined(node.colorValue) ? node.colorValue : node.value ?? 0;
651
+ if (!node.children?.length)
652
+ return [value];
653
+ return [value, ...node.children.flatMap(c => this.collectValues(c))];
654
+ }
655
+ layoutNode(node, layout) {
656
+ const children = node.children?.filter(c => !(Number.isFinite(c.value) && c.value === 0)) ?? [];
657
+ if (!children.length)
658
+ return;
659
+ const title = this.titleSizeFor(node);
660
+ const t = this.cfg.type;
661
+ const content = (t === 'horizontal')
662
+ ? { width: Math.max(0, node.coord.width - title), height: node.coord.height, top: 0, left: 0 }
663
+ : { width: node.coord.width, height: Math.max(0, node.coord.height - title), top: 0, left: 0 };
664
+ if (content.width < this.cfg.minTileSize || content.height < this.cfg.minTileSize) {
665
+ node.children = undefined;
666
+ return;
667
+ }
668
+ // compute coords for children
669
+ layout.compute(children, content);
670
+ // recurse
671
+ for (const c of children) {
672
+ if (c.coord.width < this.cfg.minTileSize || c.coord.height < this.cfg.minTileSize) {
673
+ c.children = undefined;
674
+ continue;
675
+ }
676
+ this.layoutNode(c, layout);
677
+ }
678
+ }
679
+ roundCoordsDeep(node, decimals) {
680
+ if (node.dataItem) {
681
+ node.coord = {
682
+ width: roundN(node.coord.width, decimals),
683
+ height: roundN(node.coord.height, decimals),
684
+ top: roundN(node.coord.top, decimals),
685
+ left: roundN(node.coord.left, decimals)
686
+ };
687
+ }
688
+ if (node.children?.length) {
689
+ for (const c of node.children)
690
+ this.roundCoordsDeep(c, decimals);
691
+ }
692
+ }
693
+ buildPath(node) {
694
+ if (!this.root)
695
+ return [];
696
+ const targetUid = node.uid;
697
+ const stack = [{ n: this.root, path: [] }];
698
+ while (stack.length) {
699
+ const cur = stack.pop();
700
+ const n = cur.n;
701
+ if (n.uid === targetUid)
702
+ return cur.path;
703
+ const children = n.children ?? [];
704
+ for (const c of children) {
705
+ const nextPath = c.dataItem ? [...cur.path, c.dataItem] : cur.path;
706
+ stack.push({ n: c, path: nextPath });
707
+ }
708
+ }
709
+ return [];
710
+ }
711
+ nextUid() {
712
+ this.uidSeq += 1;
713
+ return `tm_${this.uidSeq}`;
714
+ }
715
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: TreeMapComponent, deps: [{ token: i0.NgZone }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
716
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.13", type: TreeMapComponent, isStandalone: true, selector: "stockchart-treemap", inputs: { data: "data", data$: "data$", loader: "loader", refreshMs: "refreshMs", options: "options", tileTemplate: "tileTemplate", titleTemplate: "titleTemplate" }, outputs: { dataBound: "dataBound", tileClick: "tileClick", tileHover: "tileHover" }, viewQueries: [{ propertyName: "host", first: true, predicate: ["host"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: "<div #host class=\"sc-treemap k-widget\">\r\n <ng-container *ngIf=\"root as r\">\r\n <div class=\"sc-treemap-root\">\r\n <!-- \u0434\u0435\u0442\u0438 \u043F\u0441\u0435\u0432\u0434\u043E-\u043A\u043E\u0440\u043D\u044F -->\r\n <div class=\"sc-treemap-wrap sc-treemap-wrap-col\">\r\n <ng-container *ngFor=\"let child of r.children; trackBy: trackByUid\">\r\n <ng-container *ngTemplateOutlet=\"nodeTpl; context: { $implicit: child }\"></ng-container>\r\n </ng-container>\r\n </div>\r\n </div>\r\n </ng-container>\r\n</div>\r\n\r\n<ng-template #nodeTpl let-node>\r\n <!-- Leaf -->\r\n <ng-container *ngIf=\"isLeaf(node); else containerTpl\">\r\n <div\r\n class=\"sc-treemap-tile k-leaf\"\r\n [class.k-inverse]=\"isInverse(node)\"\r\n [class.k-hover]=\"hoveredUid === node.uid\"\r\n [style.left.px]=\"node.coord.left\"\r\n [style.top.px]=\"node.coord.top\"\r\n [style.width.px]=\"node.coord.width\"\r\n [style.height.px]=\"node.coord.height\"\r\n [style.backgroundColor]=\"node.color\"\r\n (mouseenter)=\"onTileMouseEnter(node)\"\r\n (mouseleave)=\"onTileMouseLeave(node)\"\r\n (click)=\"onTileClick(node)\"\r\n [attr.data-uid]=\"node.uid\"\r\n >\r\n <div class=\"sc-treemap-leaf-content\">\r\n <ng-container *ngIf=\"tileTemplate; else defaultLeaf\">\r\n <ng-container\r\n *ngTemplateOutlet=\"tileTemplate; context: { $implicit: node.dataItem, node: node, isLeaf: true }\">\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-template #defaultLeaf>\r\n <!-- \u0434\u0435\u0444\u043E\u043B\u0442 \u043F\u043E\u0434 \u0442\u0432\u043E\u0439 \u0444\u043E\u0440\u043C\u0430\u0442: name + ticker + percent -->\r\n <div class=\"leaf-meta\">{{ node.text }}</div>\r\n\r\n \r\n </ng-template>\r\n </div>\r\n </div>\r\n </ng-container>\r\n\r\n<!-- Container -->\r\n<ng-template #containerTpl>\r\n <div\r\n class=\"sc-treemap-tile k-node\"\r\n [style.left.px]=\"node.coord.left\"\r\n [style.top.px]=\"node.coord.top\"\r\n [style.width.px]=\"node.coord.width\"\r\n [style.height.px]=\"node.coord.height\"\r\n [attr.data-uid]=\"node.uid\"\r\n >\r\n <!-- TITLE: \u0443\u0431\u0438\u0440\u0430\u0435\u043C \u0438\u0437 flow -->\r\n <ng-container *ngIf=\"titleSizeFor(node) as ts\">\r\n <div\r\n class=\"sc-treemap-title\"\r\n [class.sc-treemap-title-vertical]=\"isTitleSide()\"\r\n [class.tm-title-top]=\"!isTitleSide()\"\r\n [class.tm-title-left]=\"isTitleSide()\"\r\n [style.height.px]=\"!isTitleSide() ? ts : null\"\r\n [style.width.px]=\"isTitleSide() ? ts : null\"\r\n (mouseenter)=\"onTileMouseEnter(node)\"\r\n (mouseleave)=\"onTileMouseLeave(node)\"\r\n (click)=\"onTileClick(node)\"\r\n >\r\n <ng-container *ngIf=\"titleTemplate; else defaultTitle\">\r\n <ng-container\r\n *ngTemplateOutlet=\"titleTemplate; context: { $implicit: node.dataItem, node: node, isLeaf: false }\">\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-template #defaultTitle>\r\n <div class=\"sc-treemap-title-inner\">{{ node.text }}</div>\r\n </ng-template>\r\n </div>\r\n </ng-container>\r\n\r\n <!-- WRAP: \u0437\u0430\u043D\u0438\u043C\u0430\u0435\u0442 \u0440\u043E\u0432\u043D\u043E \u043E\u0441\u0442\u0430\u0432\u0448\u0435\u0435\u0441\u044F \u043C\u0435\u0441\u0442\u043E -->\r\n <div\r\n class=\"sc-treemap-wrap sc-treemap-node-wrap\"\r\n [class.tm-wrap-top]=\"!isTitleSide()\"\r\n [class.tm-wrap-left]=\"isTitleSide()\"\r\n [style.top.px]=\"!isTitleSide() ? titleSizeFor(node) : 0\"\r\n [style.left.px]=\"isTitleSide() ? titleSizeFor(node) : 0\"\r\n >\r\n <ng-container *ngFor=\"let child of node.children; trackBy: trackByUid\">\r\n <ng-container *ngTemplateOutlet=\"nodeTpl; context: { $implicit: child }\"></ng-container>\r\n </ng-container>\r\n </div>\r\n </div>\r\n</ng-template>\r\n", styles: [":host,.sc-treemap{display:block;width:100%;height:100%}.sc-treemap{position:relative;overflow:hidden;-webkit-user-select:none;user-select:none;font-family:Arial,sans-serif}.sc-treemap-root{position:relative;width:100%;height:100%}.sc-treemap-wrap{position:relative;width:100%;height:100%;overflow:hidden;display:flex;flex:1 1 auto}.sc-treemap-wrap-col{flex-direction:column}.sc-treemap-wrap-row{flex-direction:row}.sc-treemap-tile{position:absolute;box-sizing:border-box;overflow:hidden;border:1px solid rgba(0,0,0,.08)}.sc-treemap-tile.k-node{background:#00000003;display:flex}.sc-treemap-tile.k-node.k-layout-col{flex-direction:column}.sc-treemap-tile.k-node.k-layout-row{flex-direction:row}.sc-treemap-tile.k-leaf{cursor:pointer}.sc-treemap-leaf-content{padding:8px;font-size:12px;line-height:1.2;word-break:break-word}.leaf-title{font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.leaf-meta{margin-top:4px;font-size:13px;opacity:.9;display:flex;gap:8px;justify-content:center;text-align:center}.sc-treemap-tile.k-leaf.k-hover{outline:2px solid rgba(0,0,0,.25);outline-offset:-2px}.sc-treemap-tile.k-leaf.k-inverse{color:#000}.sc-treemap-tile.k-leaf:not(.k-inverse){color:#fff}.sc-treemap-title{box-sizing:border-box;padding:6px 8px;font-size:14px;font-weight:600;background:#0000000a;color:#000000d9;flex:0 0 auto;display:flex;align-items:center;cursor:pointer;justify-content:center;text-align:center}.sc-treemap-title-inner{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sc-treemap-title-vertical{writing-mode:vertical-rl;text-orientation:mixed;padding:8px 6px}.sc-treemap-title.tm-title-top,.sc-treemap-title.tm-title-left{position:absolute;z-index:2}.tm-title-top{top:0;left:0;right:0}.tm-title-left{top:0;left:0;bottom:0}.sc-treemap-node-wrap{position:absolute;right:0;bottom:0;overflow:hidden;z-index:1;display:block}.sc-treemap-node-wrap.tm-wrap-top{left:0}.sc-treemap-node-wrap.tm-wrap-left{top:0}.sc-treemap-tile.k-node{overflow:hidden}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
717
+ }
718
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: TreeMapComponent, decorators: [{
719
+ type: Component,
720
+ args: [{ selector: 'stockchart-treemap', standalone: true, imports: [CommonModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div #host class=\"sc-treemap k-widget\">\r\n <ng-container *ngIf=\"root as r\">\r\n <div class=\"sc-treemap-root\">\r\n <!-- \u0434\u0435\u0442\u0438 \u043F\u0441\u0435\u0432\u0434\u043E-\u043A\u043E\u0440\u043D\u044F -->\r\n <div class=\"sc-treemap-wrap sc-treemap-wrap-col\">\r\n <ng-container *ngFor=\"let child of r.children; trackBy: trackByUid\">\r\n <ng-container *ngTemplateOutlet=\"nodeTpl; context: { $implicit: child }\"></ng-container>\r\n </ng-container>\r\n </div>\r\n </div>\r\n </ng-container>\r\n</div>\r\n\r\n<ng-template #nodeTpl let-node>\r\n <!-- Leaf -->\r\n <ng-container *ngIf=\"isLeaf(node); else containerTpl\">\r\n <div\r\n class=\"sc-treemap-tile k-leaf\"\r\n [class.k-inverse]=\"isInverse(node)\"\r\n [class.k-hover]=\"hoveredUid === node.uid\"\r\n [style.left.px]=\"node.coord.left\"\r\n [style.top.px]=\"node.coord.top\"\r\n [style.width.px]=\"node.coord.width\"\r\n [style.height.px]=\"node.coord.height\"\r\n [style.backgroundColor]=\"node.color\"\r\n (mouseenter)=\"onTileMouseEnter(node)\"\r\n (mouseleave)=\"onTileMouseLeave(node)\"\r\n (click)=\"onTileClick(node)\"\r\n [attr.data-uid]=\"node.uid\"\r\n >\r\n <div class=\"sc-treemap-leaf-content\">\r\n <ng-container *ngIf=\"tileTemplate; else defaultLeaf\">\r\n <ng-container\r\n *ngTemplateOutlet=\"tileTemplate; context: { $implicit: node.dataItem, node: node, isLeaf: true }\">\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-template #defaultLeaf>\r\n <!-- \u0434\u0435\u0444\u043E\u043B\u0442 \u043F\u043E\u0434 \u0442\u0432\u043E\u0439 \u0444\u043E\u0440\u043C\u0430\u0442: name + ticker + percent -->\r\n <div class=\"leaf-meta\">{{ node.text }}</div>\r\n\r\n \r\n </ng-template>\r\n </div>\r\n </div>\r\n </ng-container>\r\n\r\n<!-- Container -->\r\n<ng-template #containerTpl>\r\n <div\r\n class=\"sc-treemap-tile k-node\"\r\n [style.left.px]=\"node.coord.left\"\r\n [style.top.px]=\"node.coord.top\"\r\n [style.width.px]=\"node.coord.width\"\r\n [style.height.px]=\"node.coord.height\"\r\n [attr.data-uid]=\"node.uid\"\r\n >\r\n <!-- TITLE: \u0443\u0431\u0438\u0440\u0430\u0435\u043C \u0438\u0437 flow -->\r\n <ng-container *ngIf=\"titleSizeFor(node) as ts\">\r\n <div\r\n class=\"sc-treemap-title\"\r\n [class.sc-treemap-title-vertical]=\"isTitleSide()\"\r\n [class.tm-title-top]=\"!isTitleSide()\"\r\n [class.tm-title-left]=\"isTitleSide()\"\r\n [style.height.px]=\"!isTitleSide() ? ts : null\"\r\n [style.width.px]=\"isTitleSide() ? ts : null\"\r\n (mouseenter)=\"onTileMouseEnter(node)\"\r\n (mouseleave)=\"onTileMouseLeave(node)\"\r\n (click)=\"onTileClick(node)\"\r\n >\r\n <ng-container *ngIf=\"titleTemplate; else defaultTitle\">\r\n <ng-container\r\n *ngTemplateOutlet=\"titleTemplate; context: { $implicit: node.dataItem, node: node, isLeaf: false }\">\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-template #defaultTitle>\r\n <div class=\"sc-treemap-title-inner\">{{ node.text }}</div>\r\n </ng-template>\r\n </div>\r\n </ng-container>\r\n\r\n <!-- WRAP: \u0437\u0430\u043D\u0438\u043C\u0430\u0435\u0442 \u0440\u043E\u0432\u043D\u043E \u043E\u0441\u0442\u0430\u0432\u0448\u0435\u0435\u0441\u044F \u043C\u0435\u0441\u0442\u043E -->\r\n <div\r\n class=\"sc-treemap-wrap sc-treemap-node-wrap\"\r\n [class.tm-wrap-top]=\"!isTitleSide()\"\r\n [class.tm-wrap-left]=\"isTitleSide()\"\r\n [style.top.px]=\"!isTitleSide() ? titleSizeFor(node) : 0\"\r\n [style.left.px]=\"isTitleSide() ? titleSizeFor(node) : 0\"\r\n >\r\n <ng-container *ngFor=\"let child of node.children; trackBy: trackByUid\">\r\n <ng-container *ngTemplateOutlet=\"nodeTpl; context: { $implicit: child }\"></ng-container>\r\n </ng-container>\r\n </div>\r\n </div>\r\n</ng-template>\r\n", styles: [":host,.sc-treemap{display:block;width:100%;height:100%}.sc-treemap{position:relative;overflow:hidden;-webkit-user-select:none;user-select:none;font-family:Arial,sans-serif}.sc-treemap-root{position:relative;width:100%;height:100%}.sc-treemap-wrap{position:relative;width:100%;height:100%;overflow:hidden;display:flex;flex:1 1 auto}.sc-treemap-wrap-col{flex-direction:column}.sc-treemap-wrap-row{flex-direction:row}.sc-treemap-tile{position:absolute;box-sizing:border-box;overflow:hidden;border:1px solid rgba(0,0,0,.08)}.sc-treemap-tile.k-node{background:#00000003;display:flex}.sc-treemap-tile.k-node.k-layout-col{flex-direction:column}.sc-treemap-tile.k-node.k-layout-row{flex-direction:row}.sc-treemap-tile.k-leaf{cursor:pointer}.sc-treemap-leaf-content{padding:8px;font-size:12px;line-height:1.2;word-break:break-word}.leaf-title{font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.leaf-meta{margin-top:4px;font-size:13px;opacity:.9;display:flex;gap:8px;justify-content:center;text-align:center}.sc-treemap-tile.k-leaf.k-hover{outline:2px solid rgba(0,0,0,.25);outline-offset:-2px}.sc-treemap-tile.k-leaf.k-inverse{color:#000}.sc-treemap-tile.k-leaf:not(.k-inverse){color:#fff}.sc-treemap-title{box-sizing:border-box;padding:6px 8px;font-size:14px;font-weight:600;background:#0000000a;color:#000000d9;flex:0 0 auto;display:flex;align-items:center;cursor:pointer;justify-content:center;text-align:center}.sc-treemap-title-inner{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sc-treemap-title-vertical{writing-mode:vertical-rl;text-orientation:mixed;padding:8px 6px}.sc-treemap-title.tm-title-top,.sc-treemap-title.tm-title-left{position:absolute;z-index:2}.tm-title-top{top:0;left:0;right:0}.tm-title-left{top:0;left:0;bottom:0}.sc-treemap-node-wrap{position:absolute;right:0;bottom:0;overflow:hidden;z-index:1;display:block}.sc-treemap-node-wrap.tm-wrap-top{left:0}.sc-treemap-node-wrap.tm-wrap-left{top:0}.sc-treemap-tile.k-node{overflow:hidden}\n"] }]
721
+ }], ctorParameters: () => [{ type: i0.NgZone }, { type: i0.ChangeDetectorRef }], propDecorators: { host: [{
722
+ type: ViewChild,
723
+ args: ['host', { static: true }]
724
+ }], data: [{
725
+ type: Input
726
+ }], data$: [{
727
+ type: Input
728
+ }], loader: [{
729
+ type: Input
730
+ }], refreshMs: [{
731
+ type: Input
732
+ }], options: [{
733
+ type: Input
734
+ }], tileTemplate: [{
735
+ type: Input
736
+ }], titleTemplate: [{
737
+ type: Input
738
+ }], dataBound: [{
739
+ type: Output
740
+ }], tileClick: [{
741
+ type: Output
742
+ }], tileHover: [{
743
+ type: Output
744
+ }] } });
745
+
746
+ /*
747
+ * Public API Surface of stockchart-treemap
748
+ */
749
+
750
+ /**
751
+ * Generated bundle index. Do not edit.
752
+ */
753
+
754
+ export { SliceAndDiceLayout, SquarifiedLayout, TreeMapComponent, colorBrightness, colorsByLength, defined, getField, projectColorByValue, round, roundN, toNumber, totalAreaOf };
755
+ //# sourceMappingURL=stockchart-treemap.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stockchart-treemap.mjs","sources":["../../../projects/stockchart-treemap/src/lib/tree-map/tree-map.utils.ts","../../../projects/stockchart-treemap/src/lib/tree-map/tree-map.layouts.ts","../../../projects/stockchart-treemap/src/lib/tree-map/tree-map.component.ts","../../../projects/stockchart-treemap/src/lib/tree-map/tree-map.component.html","../../../projects/stockchart-treemap/src/public-api.ts","../../../projects/stockchart-treemap/src/stockchart-treemap.ts"],"sourcesContent":["import { TreeMapColorScale, TreeNode } from './tree-map.models';\r\n\r\nconst UNDEFINED = 'undefined';\r\n\r\nexport function getField(path: string, row: any): any {\r\n if (row === null || row === undefined) return row;\r\n const parts = String(path ?? '').split('.').filter(Boolean);\r\n let cur = row;\r\n for (const p of parts) {\r\n if (cur === null || cur === undefined) return undefined;\r\n cur = cur[p];\r\n }\r\n return cur;\r\n}\r\n\r\nexport function defined(value: any): boolean {\r\n return typeof value !== UNDEFINED;\r\n}\r\n\r\nexport function toNumber(v: any): number {\r\n if (v === null || v === undefined) return 0;\r\n const n = typeof v === 'number' ? v : parseFloat(String(v).replace(',', '.'));\r\n return Number.isFinite(n) ? n : 0;\r\n}\r\n\r\nexport function totalAreaOf(items: TreeNode[]): number {\r\n let total = 0;\r\n for (let i = 0; i < items.length; i++) total += (items[i].area ?? 0);\r\n return total;\r\n}\r\n\r\nexport function round(value: number): number {\r\n const power = Math.pow(10, 4);\r\n return Math.round(value * power) / power;\r\n}\r\n\r\nexport function roundN(value: number, decimals: number): number {\r\n const power = Math.pow(10, decimals);\r\n return Math.round(value * power) / power;\r\n}\r\n\r\nexport function colorsByLength(min: string, max: string, length: number): string[] {\r\n const minRGBtoDecimal = rgbToDecimal(min);\r\n const maxRGBtoDecimal = rgbToDecimal(max);\r\n const isDarker = colorBrightness(min) - colorBrightness(max) < 0;\r\n const colors: string[] = [];\r\n\r\n colors.push(min);\r\n\r\n for (let i = 0; i < length; i++) {\r\n const rgbColor = {\r\n r: colorByIndex(minRGBtoDecimal.r, maxRGBtoDecimal.r, i, length, isDarker),\r\n g: colorByIndex(minRGBtoDecimal.g, maxRGBtoDecimal.g, i, length, isDarker),\r\n b: colorByIndex(minRGBtoDecimal.b, maxRGBtoDecimal.b, i, length, isDarker)\r\n };\r\n colors.push(buildColorFromRGB(rgbColor));\r\n }\r\n\r\n colors.push(max);\r\n return colors;\r\n}\r\n\r\nexport function projectColorByValue(value: number, min: number, max: number, scale: TreeMapColorScale): string {\r\n const hasCenter = typeof scale.center === 'string';\r\n const clampedMin = Math.min(min, max);\r\n const clampedMax = Math.max(min, max);\r\n\r\n if (clampedMin === clampedMax) {\r\n return scale.center ?? scale.max ?? scale.min;\r\n }\r\n\r\n if (hasCenter && clampedMin < 0 && clampedMax > 0) {\r\n if (value >= 0) {\r\n const ratio = clampedMax === 0 ? 0 : clamp01(value / clampedMax);\r\n return interpolateColor(scale.center ?? scale.min, scale.max, ratio);\r\n }\r\n const ratio = clampedMin === 0 ? 0 : clamp01(value / clampedMin);\r\n return interpolateColor(scale.center ?? scale.max, scale.min, ratio);\r\n }\r\n\r\n const ratio = clamp01((value - clampedMin) / (clampedMax - clampedMin));\r\n return interpolateColor(scale.min, scale.max, ratio);\r\n}\r\n\r\nfunction colorByIndex(min: number, max: number, index: number, length: number, isDarker: boolean): number {\r\n const minColor = Math.min(Math.abs(min), Math.abs(max));\r\n const maxColor = Math.max(Math.abs(min), Math.abs(max));\r\n const step = (maxColor - minColor) / (length + 1);\r\n const currentStep = step * (index + 1);\r\n\r\n return isDarker ? (minColor + currentStep) : (maxColor - currentStep);\r\n}\r\n\r\nfunction interpolateColor(min: string, max: string, ratio: number): string {\r\n const start = rgbToDecimal(min);\r\n const end = rgbToDecimal(max);\r\n const t = clamp01(ratio);\r\n\r\n return buildColorFromRGB({\r\n r: lerp(start.r, end.r, t),\r\n g: lerp(start.g, end.g, t),\r\n b: lerp(start.b, end.b, t)\r\n });\r\n}\r\n\r\nfunction buildColorFromRGB(color: { r: number; g: number; b: number }): string {\r\n return '#' + decimalToRgb(color.r) + decimalToRgb(color.g) + decimalToRgb(color.b);\r\n}\r\n\r\nfunction lerp(from: number, to: number, t: number): number {\r\n return from + (to - from) * t;\r\n}\r\n\r\nfunction rgbToDecimal(color: string): { r: number; g: number; b: number } {\r\n color = color.replace('#', '');\r\n const rgbColor = colorToRGB(color);\r\n\r\n return {\r\n r: rgbToHex(rgbColor.r),\r\n g: rgbToHex(rgbColor.g),\r\n b: rgbToHex(rgbColor.b)\r\n };\r\n}\r\n\r\nfunction decimalToRgb(number: number): string {\r\n let result = Math.round(number).toString(16).toUpperCase();\r\n if (result.length === 1) result = '0' + result;\r\n return result;\r\n}\r\n\r\nfunction colorToRGB(color: string): { r: string; g: string; b: string } {\r\n const colorLength = color.length;\r\n const rgbColor: any = {};\r\n if (colorLength === 3) {\r\n rgbColor.r = color[0];\r\n rgbColor.g = color[1];\r\n rgbColor.b = color[2];\r\n } else {\r\n rgbColor.r = color.substring(0, 2);\r\n rgbColor.g = color.substring(2, 4);\r\n rgbColor.b = color.substring(4, 6);\r\n }\r\n return rgbColor;\r\n}\r\n\r\nfunction rgbToHex(rgb: string): number {\r\n return parseInt((rgb as any).toString(16), 16);\r\n}\r\n\r\nexport function colorBrightness(color?: string): number {\r\n let brightness = 0;\r\n if (color) {\r\n const c = rgbToDecimal(color);\r\n brightness = Math.sqrt(0.241 * c.r * c.r + 0.691 * c.g * c.g + 0.068 * c.b * c.b);\r\n }\r\n return brightness;\r\n}\r\n\r\nfunction clamp01(value: number): number {\r\n if (!Number.isFinite(value)) return 0;\r\n return Math.min(1, Math.max(0, value));\r\n}\r\n","import { Coord, Layout, TreeNode } from './tree-map.models';\r\nimport { round, totalAreaOf } from './tree-map.utils';\r\n\r\nconst MAX_VALUE = Number.MAX_VALUE;\r\n\r\nexport class SquarifiedLayout implements Layout {\r\n private orientation: 'h' | 'v' = 'h';\r\n\r\n compute(children: TreeNode[], coord: Coord): void {\r\n if (!(coord.width >= coord.height && this.layoutHorizontal())) {\r\n this.layoutChange();\r\n }\r\n if (children.length > 0) this.layoutChildren(children, coord);\r\n }\r\n\r\n private layoutChildren(items: TreeNode[], coord: Coord): void {\r\n const parentArea = coord.width * coord.height;\r\n let totalArea = 0;\r\n const itemsArea: number[] = [];\r\n\r\n for (let i = 0; i < items.length; i++) {\r\n itemsArea[i] = parseFloat(String(items[i].value ?? 0));\r\n totalArea += itemsArea[i];\r\n }\r\n if (totalArea <= 0) return;\r\n\r\n for (let i = 0; i < itemsArea.length; i++) {\r\n items[i].area = parentArea * itemsArea[i] / totalArea;\r\n }\r\n\r\n const minimumSideValue = this.layoutHorizontal() ? coord.height : coord.width;\r\n\r\n const firstElement = [items[0]];\r\n const tail = items.slice(1);\r\n this.squarify(tail, firstElement, minimumSideValue, coord);\r\n }\r\n\r\n private squarify(tail: TreeNode[], initElement: TreeNode[], width: number, coord: Coord): void {\r\n this.computeDim(tail, initElement, width, coord);\r\n }\r\n\r\n private computeDim(tail: TreeNode[], initElement: TreeNode[], width: number, coord: Coord): void {\r\n if (tail.length + initElement.length === 1) {\r\n const element = tail.length === 1 ? tail : initElement;\r\n this.layoutLast(element, coord);\r\n return;\r\n }\r\n\r\n if (tail.length >= 2 && initElement.length === 0) {\r\n initElement = [tail[0]];\r\n tail = tail.slice(1);\r\n }\r\n\r\n if (tail.length === 0) {\r\n if (initElement.length > 0) this.layoutRow(initElement, width, coord);\r\n return;\r\n }\r\n\r\n const firstElement = tail[0];\r\n\r\n if (this.worstAspectRatio(initElement, width) >= this.worstAspectRatio([firstElement, ...initElement], width)) {\r\n this.computeDim(tail.slice(1), [...initElement, firstElement], width, coord);\r\n } else {\r\n const newCoords = this.layoutRow(initElement, width, coord);\r\n this.computeDim(tail, [], newCoords.dim, newCoords);\r\n }\r\n }\r\n\r\n private layoutLast(items: TreeNode[], coord: Coord): void {\r\n items[0].coord = coord;\r\n }\r\n\r\n private layoutRow(items: TreeNode[], width: number, coord: Coord): (Coord & { dim: number }) {\r\n return this.layoutHorizontal() ? this.layoutV(items, width, coord) : this.layoutH(items, width, coord);\r\n }\r\n\r\n private layoutVertical(): boolean { return this.orientation === 'v'; }\r\n private layoutHorizontal(): boolean { return this.orientation === 'h'; }\r\n\r\n private layoutChange(): void {\r\n this.orientation = this.layoutVertical() ? 'h' : 'v';\r\n }\r\n\r\n private worstAspectRatio(items: TreeNode[], width: number): number {\r\n if (!items || items.length === 0) return MAX_VALUE;\r\n\r\n let areaSum = 0;\r\n let maxArea = 0;\r\n let minArea = MAX_VALUE;\r\n\r\n for (let i = 0; i < items.length; i++) {\r\n const area = items[i].area ?? 0;\r\n areaSum += area;\r\n minArea = (minArea < area) ? minArea : area;\r\n maxArea = (maxArea > area) ? maxArea : area;\r\n }\r\n if (areaSum <= 0) return MAX_VALUE;\r\n\r\n return Math.max(\r\n (width * width * maxArea) / (areaSum * areaSum),\r\n (areaSum * areaSum) / (width * width * minArea)\r\n );\r\n }\r\n\r\n private layoutV(items: TreeNode[], width: number, coord: Coord): (Coord & { dim: number }) {\r\n const totalArea = totalAreaOf(items);\r\n let top = 0;\r\n\r\n width = round(totalArea / width);\r\n\r\n for (let i = 0; i < items.length; i++) {\r\n const height = round((items[i].area ?? 0) / width);\r\n items[i].coord = {\r\n height,\r\n width,\r\n top: coord.top + top,\r\n left: coord.left\r\n };\r\n top += height;\r\n }\r\n\r\n const ans: Coord & { dim: number } = {\r\n height: coord.height,\r\n width: coord.width - width,\r\n top: coord.top,\r\n left: coord.left + width,\r\n dim: 0\r\n };\r\n\r\n ans.dim = Math.min(ans.width, ans.height);\r\n if (ans.dim !== ans.height) this.layoutChange();\r\n return ans;\r\n }\r\n\r\n private layoutH(items: TreeNode[], width: number, coord: Coord): (Coord & { dim: number }) {\r\n const totalArea = totalAreaOf(items);\r\n\r\n const height = round(totalArea / width);\r\n const top = coord.top;\r\n let left = 0;\r\n\r\n for (let i = 0; i < items.length; i++) {\r\n items[i].coord = {\r\n height,\r\n width: round((items[i].area ?? 0) / height),\r\n top,\r\n left: coord.left + left\r\n };\r\n left += items[i].coord.width;\r\n }\r\n\r\n const ans: Coord & { dim: number } = {\r\n height: coord.height - height,\r\n width: coord.width,\r\n top: coord.top + height,\r\n left: coord.left,\r\n dim: 0\r\n };\r\n\r\n ans.dim = Math.min(ans.width, ans.height);\r\n if (ans.dim !== ans.width) this.layoutChange();\r\n return ans;\r\n }\r\n}\r\n\r\nexport class SliceAndDiceLayout implements Layout {\r\n private vertical: boolean;\r\n private quotient: number;\r\n\r\n constructor(vertical: boolean) {\r\n this.vertical = vertical;\r\n this.quotient = vertical ? 1 : 0;\r\n }\r\n\r\n compute(children: TreeNode[], coord: Coord): void {\r\n if (children.length === 0) return;\r\n\r\n const parentArea = coord.width * coord.height;\r\n let totalArea = 0;\r\n const itemsArea: number[] = [];\r\n\r\n for (let i = 0; i < children.length; i++) {\r\n itemsArea[i] = parseFloat(String(children[i].value ?? 0));\r\n totalArea += itemsArea[i];\r\n children[i].vertical = this.vertical;\r\n }\r\n if (totalArea <= 0) return;\r\n\r\n for (let i = 0; i < itemsArea.length; i++) {\r\n children[i].area = parentArea * itemsArea[i] / totalArea;\r\n }\r\n\r\n this.sliceAndDice(children, coord);\r\n }\r\n\r\n private sliceAndDice(items: TreeNode[], coord: Coord): void {\r\n const totalArea = totalAreaOf(items);\r\n if ((items[0].level % 2) === this.quotient) {\r\n this.layoutHorizontal(items, coord, totalArea);\r\n } else {\r\n this.layoutVertical(items, coord, totalArea);\r\n }\r\n }\r\n\r\n private layoutHorizontal(items: TreeNode[], coord: Coord, totalArea: number): void {\r\n let left = 0;\r\n\r\n for (let i = 0; i < items.length; i++) {\r\n const item = items[i];\r\n const width = (item.area ?? 0) / (totalArea / coord.width);\r\n\r\n item.coord = {\r\n height: coord.height,\r\n width,\r\n top: coord.top,\r\n left: coord.left + left\r\n };\r\n left += width;\r\n }\r\n }\r\n\r\n private layoutVertical(items: TreeNode[], coord: Coord, totalArea: number): void {\r\n let top = 0;\r\n\r\n for (let i = 0; i < items.length; i++) {\r\n const item = items[i];\r\n const height = (item.area ?? 0) / (totalArea / coord.height);\r\n\r\n item.coord = {\r\n height,\r\n width: coord.width,\r\n top: coord.top + top,\r\n left: coord.left\r\n };\r\n top += height;\r\n }\r\n }\r\n}\r\n","import {\r\n AfterViewInit,\r\n ChangeDetectionStrategy,\r\n ChangeDetectorRef,\r\n Component,\r\n ElementRef,\r\n EventEmitter,\r\n Input,\r\n NgZone,\r\n OnChanges,\r\n OnDestroy,\r\n Output,\r\n SimpleChanges,\r\n TemplateRef,\r\n ViewChild\r\n} from '@angular/core';\r\nimport { CommonModule } from '@angular/common';\r\nimport { Observable, Subscription, defer, from, isObservable, of, timer } from 'rxjs';\r\nimport { switchMap } from 'rxjs/operators';\r\nimport { Coord, Layout, TreeMapEvent, TreeMapOptions, TreeMapTileContext, TreeNode } from './tree-map.models';\r\nimport { SliceAndDiceLayout, SquarifiedLayout } from './tree-map.layouts';\r\nimport { colorBrightness, colorsByLength, defined, getField, projectColorByValue, roundN, toNumber } from './tree-map.utils';\r\n\r\n@Component({\r\n selector: 'stockchart-treemap',\r\n standalone: true,\r\n imports: [CommonModule],\r\n templateUrl: './tree-map.component.html',\r\n styleUrls: ['./tree-map.component.css'],\r\n changeDetection: ChangeDetectionStrategy.OnPush\r\n \r\n})\r\nexport class TreeMapComponent<T = any> implements AfterViewInit, OnChanges, OnDestroy {\r\n @ViewChild('host', { static: true }) host!: ElementRef<HTMLElement>;\r\n\r\n // 1) Прямые данные\r\n @Input() data: T[] | null = null;\r\n\r\n // 2) Поток данных (обновления пушатся)\r\n @Input() data$?: Observable<T[]>;\r\n\r\n // 3) Лоадер (компонент сам вызывает). Можно плюс polling.\r\n @Input() loader?: () => Observable<T[]> | Promise<T[]> | T[];\r\n @Input() refreshMs: number | null = null; // например 5000. null/0 = без polling\r\n\r\n @Input() options: Partial<TreeMapOptions> = {};\r\n\r\n @Input() tileTemplate?: TemplateRef<TreeMapTileContext<T>>;\r\n @Input() titleTemplate?: TemplateRef<TreeMapTileContext<T>>;\r\n\r\n @Output() dataBound = new EventEmitter<void>();\r\n @Output() tileClick = new EventEmitter<TreeMapEvent<T>>();\r\n @Output() tileHover = new EventEmitter<TreeMapEvent<T>>();\r\n\r\n root: TreeNode<T> | null = null;\r\n hoveredUid: string | null = null;\r\n\r\n private ro: ResizeObserver | null = null;\r\n private rebuildQueued = false;\r\n\r\n private uidSeq = 0;\r\n private colorIdx = 0;\r\n\r\n private sourceSub: Subscription | null = null;\r\n\r\n private get cfg(): TreeMapOptions {\r\n const valueField = this.options.valueField ?? 'value';\r\n const colorValueField = this.options.colorValueField ?? valueField;\r\n\r\n return {\r\n type: 'squarified',\r\n textField: 'name',\r\n valueField,\r\n colorField: 'color',\r\n colorValueField,\r\n childrenField: 'items',\r\n colors: ['#5B8FF9', '#5AD8A6', '#5D7092', '#F6BD16', '#E8684A', '#6DC8EC'],\r\n colorScale: undefined,\r\n titleSize: 26,\r\n showTopLevelTitles: true,\r\n deriveParentValueFromChildren: true,\r\n roundDecimals: 4,\r\n minTileSize: 2,\r\n ...this.options\r\n };\r\n }\r\n\r\n\r\n constructor(private zone: NgZone, private cdr: ChangeDetectorRef) {}\r\n\r\n\r\n ngAfterViewInit(): void {\r\n \r\n this.zone.runOutsideAngular(() => {\r\n this.ro = new ResizeObserver(() => this.queueRebuild());\r\n this.ro.observe(this.host.nativeElement);\r\n });\r\n\r\n this.resetDataSource();\r\n this.rebuild(); // на случай, если data уже есть\r\n }\r\n\r\n ngOnChanges(changes: SimpleChanges): void {\r\n if (changes['data$'] || changes['loader'] || changes['refreshMs']) {\r\n this.resetDataSource();\r\n }\r\n if (changes['data'] || changes['options']) {\r\n this.queueRebuild();\r\n }\r\n }\r\n\r\n ngOnDestroy(): void {\r\n this.ro?.disconnect();\r\n this.ro = null;\r\n this.sourceSub?.unsubscribe();\r\n this.sourceSub = null;\r\n }\r\n\r\n // ===== Template helpers =====\r\n isLeaf(node: TreeNode<T>): boolean {\r\n return !(node.children && node.children.length);\r\n }\r\n\r\n isInverse(node: TreeNode<T>): boolean {\r\n return colorBrightness(node.color) > 180;\r\n }\r\n\r\n isTitleSide(): boolean {\r\n return this.cfg.type === 'horizontal';\r\n }\r\n\r\n trackByUid = (_: number, n: TreeNode<T>) => n.uid;\r\n\r\n titleSizeFor(node: TreeNode<T>): number {\r\n // Псевдо-корень (level 0, dataItem=null) не имеет заголовка\r\n if (!node.dataItem) return 0;\r\n const hasChildren = !!node.children?.length;\r\n if (!hasChildren) return 0;\r\n // top-level показываем/скрываем опцией\r\n if (node.level === 1 && !this.cfg.showTopLevelTitles) return 0;\r\n const hasText = (node.text ?? '').trim().length > 0;\r\n const hasTpl = !!this.titleTemplate;\r\n if (!hasText && !hasTpl) return 0;\r\n return Math.max(0, this.cfg.titleSize);\r\n }\r\n\r\nonTileMouseEnter(node: TreeNode<T>): void {\r\n if (!node.dataItem) return;\r\n this.hoveredUid = node.uid;\r\n this.tileHover.emit({ node, dataItem: node.dataItem, path: this.buildPath(node) });\r\n}\r\n\r\nonTileMouseLeave(node: TreeNode<T>): void {\r\n if (this.hoveredUid === node.uid) this.hoveredUid = null;\r\n}\r\n\r\nonTileClick(node: TreeNode<T>): void {\r\n if (!node.dataItem) return;\r\n this.tileClick.emit({ node, dataItem: node.dataItem, path: this.buildPath(node) });\r\n}\r\n\r\n\r\n /** Можно дергать из родителя: this.treemap.refreshNow() */\r\n refreshNow(): void {\r\n // если источник data$ — просто rebuild\r\n // если loader — принудительно дернем еще раз\r\n if (this.loader && !this.data$) {\r\n this.resetDataSource();\r\n return;\r\n }\r\n this.queueRebuild();\r\n }\r\n\r\n // ===== Data source wiring =====\r\n private resetDataSource(): void {\r\n \r\n this.sourceSub?.unsubscribe();\r\n this.sourceSub = null;\r\n\r\n // Приоритет: data$ > loader > data\r\n if (this.data$) {\r\n this.sourceSub = this.data$.subscribe(arr => {\r\n this.data = arr ?? [];\r\n this.queueRebuild();\r\n });\r\n return;\r\n }\r\n\r\n if (this.loader) {\r\n const oneLoad$ = defer(() => {\r\n const res = this.loader!();\r\n if (isObservable(res)) return res;\r\n if (res instanceof Promise) return from(res);\r\n return of(res as T[]);\r\n });\r\n\r\n const poll = this.refreshMs && this.refreshMs > 0;\r\n const stream$ = poll\r\n ? timer(0, this.refreshMs!).pipe(switchMap(() => oneLoad$))\r\n : oneLoad$;\r\n\r\n this.sourceSub = stream$.subscribe(arr => {\r\n this.data = arr ?? [];\r\nthis.cdr.markForCheck();\r\nthis.queueRebuild();\r\n });\r\n }\r\n }\r\n\r\n // ===== Layout pipeline =====\r\n private queueRebuild(): void {\r\n if (this.rebuildQueued) return;\r\n this.rebuildQueued = true;\r\n\r\n queueMicrotask(() => {\r\n this.rebuildQueued = false;\r\n this.rebuild();\r\n });\r\n }\r\n\r\n private rebuild(): void {\r\n\r\n const el = this.host?.nativeElement;\r\n if (!el) return;\r\n\r\n const rect = el.getBoundingClientRect();\r\n const width = Math.max(0, rect.width);\r\n const height = Math.max(0, rect.height);\r\n if (width === 0 || height === 0) return;\r\n\r\n this.uidSeq = 0;\r\n this.colorIdx = 0;\r\n\r\n const root: TreeNode<T> = {\r\n uid: this.nextUid(),\r\n level: 0,\r\n text: '',\r\n value: 0,\r\n coord: { width, height, top: 0, left: 0 },\r\n dataItem: null,\r\n children: this.buildNodes(this.data ?? [], 1)\r\n };\r\n\r\n this.sortTree(root);\r\n\r\n if (this.cfg.deriveParentValueFromChildren) {\r\n this.deriveValues(root);\r\n }\r\n\r\n this.applyColors(root);\r\n\r\n const layout: Layout =\r\n this.cfg.type === 'vertical' ? new SliceAndDiceLayout(true) :\r\n this.cfg.type === 'horizontal' ? new SliceAndDiceLayout(false) :\r\n new SquarifiedLayout();\r\n\r\n this.layoutNode(root, layout);\r\n\r\n // округление координат (как round в исходнике)\r\n const d = this.cfg.roundDecimals;\r\n this.roundCoordsDeep(root, d);\r\n\r\n this.root = root;\r\nthis.cdr.markForCheck(); // <-- важно\r\n\r\n this.zone.run(() => this.dataBound.emit());\r\n }\r\n\r\n private buildNodes(items: T[], level: number): TreeNode<T>[] {\r\n\r\n const { textField, valueField, colorField, colorValueField, childrenField } = this.cfg;\r\n\r\n const nodes: TreeNode<T>[] = [];\r\n for (const item of items ?? []) {\r\n const rawText = getField(textField, item);\r\n const rawValue = getField(valueField, item);\r\n const rawColor = getField(colorField, item);\r\n const rawColorValue = getField(colorValueField, item);\r\n const rawChildren = getField(childrenField, item);\r\n\r\n const childrenArr = Array.isArray(rawChildren) ? (rawChildren as T[]) : [];\r\n\r\n const node: TreeNode<T> = {\r\n uid: this.nextUid(),\r\n level,\r\n text: (rawText ?? '') + '',\r\n value: toNumber(rawValue),\r\n colorValue: toNumber(rawColorValue),\r\n color: typeof rawColor === 'string' ? rawColor : undefined,\r\n coord: { width: 0, height: 0, top: 0, left: 0 },\r\n dataItem: item,\r\n children: childrenArr.length ? this.buildNodes(childrenArr, level + 1) : undefined\r\n };\r\n\r\n // Пропускаем только \"пустые\" листья без детей и нулевой value; контейнеры с children оставляем,\r\n // чтобы значение пересчиталось от потомков и дерево не опустело (случай value=0 у корня данных).\r\n if (Number.isFinite(node.value) && node.value === 0 && !childrenArr.length) continue;\r\n\r\n nodes.push(node);\r\n }\r\n\r\n return nodes;\r\n }\r\n\r\n private sortTree(node: TreeNode<T>): void {\r\n if (!node.children?.length) return;\r\n node.children.sort((a, b) => (b.value ?? 0) - (a.value ?? 0));\r\n for (const c of node.children) this.sortTree(c);\r\n }\r\n\r\n private deriveValues(node: TreeNode<T>): number {\r\n if (!node.children?.length) {\r\n return Number.isFinite(node.value) ? node.value : 0;\r\n }\r\n const sum = node.children.reduce((acc, c) => acc + this.deriveValues(c), 0);\r\n if (!Number.isFinite(node.value) || node.value === 0) node.value = sum;\r\n return node.value;\r\n }\r\n\r\n private applyColors(node: TreeNode<T>): void {\r\n const items = node.children;\r\n if (!items?.length) return;\r\n\r\n if (this.cfg.colorScale) {\r\n this.applyProjectedColors(items, this.cfg.colorScale);\r\n }\r\n\r\n const colors = this.cfg.colors;\r\n if (colors.length) {\r\n this.applyPaletteColors(items, colors);\r\n } else {\r\n this.applyFallbackColors(items);\r\n }\r\n\r\n for (const c of items) this.applyColors(c);\r\n }\r\n\r\n private applyProjectedColors(nodes: TreeNode<T>[], scale: NonNullable<TreeMapOptions['colorScale']>): void {\r\n const values = nodes.flatMap(n => this.collectValues(n));\r\n if (!values.length) return;\r\n\r\n const min = Math.min(...values);\r\n const max = Math.max(...values);\r\n\r\n const allNodes = nodes.flatMap(n => this.collectNodes(n));\r\n for (const n of allNodes) {\r\n if (!defined(n.color)) {\r\n const colorValue = defined(n.colorValue) ? n.colorValue! : n.value ?? 0;\r\n n.color = projectColorByValue(colorValue, min, max, scale);\r\n }\r\n }\r\n }\r\n\r\n private applyPaletteColors(nodes: TreeNode<T>[], colors: Array<string | [string, string]>): void {\r\n if (!nodes.length) return;\r\n\r\n const chosen = colors[this.colorIdx % colors.length];\r\n let colorRange: string[] | null = null;\r\n\r\n // ВАЖНО: намеренно повторяем логику из твоего кода:\r\n // colorsByLength возвращает length+2, а мы берём первые length элементов.\r\n if (Array.isArray(chosen)) {\r\n colorRange = colorsByLength(chosen[0], chosen[1], nodes.length);\r\n }\r\n\r\n let leafNodes = false;\r\n for (let i = 0; i < nodes.length; i++) {\r\n const node = nodes[i];\r\n\r\n if (!defined(node.color)) {\r\n node.color = colorRange ? colorRange[i] : (chosen as string);\r\n }\r\n\r\n if (!node.children?.length) leafNodes = true;\r\n }\r\n\r\n if (leafNodes) this.colorIdx++;\r\n }\r\n\r\n private applyFallbackColors(nodes: TreeNode<T>[]): void {\r\n for (const n of nodes) {\r\n if (!defined(n.color)) n.color = '#B0B0B0';\r\n }\r\n }\r\n\r\n private collectNodes(node: TreeNode<T>): TreeNode<T>[] {\r\n const res: TreeNode<T>[] = [node];\r\n for (const c of node.children ?? []) res.push(...this.collectNodes(c));\r\n return res;\r\n }\r\n\r\n private collectValues(node: TreeNode<T>): number[] {\r\n const value = defined(node.colorValue) ? node.colorValue! : node.value ?? 0;\r\n if (!node.children?.length) return [value];\r\n return [value, ...node.children.flatMap(c => this.collectValues(c))];\r\n }\r\n\r\n private layoutNode(node: TreeNode<T>, layout: Layout): void {\r\n const children = node.children?.filter(c => !(Number.isFinite(c.value) && c.value === 0)) ?? [];\r\n if (!children.length) return;\r\n\r\n const title = this.titleSizeFor(node);\r\n const t = this.cfg.type;\r\n\r\n const content: Coord =\r\n (t === 'horizontal')\r\n ? { width: Math.max(0, node.coord.width - title), height: node.coord.height, top: 0, left: 0 }\r\n : { width: node.coord.width, height: Math.max(0, node.coord.height - title), top: 0, left: 0 };\r\n\r\n if (content.width < this.cfg.minTileSize || content.height < this.cfg.minTileSize) {\r\n node.children = undefined;\r\n return;\r\n }\r\n\r\n // compute coords for children\r\n layout.compute(children, content);\r\n\r\n // recurse\r\n for (const c of children) {\r\n if (c.coord.width < this.cfg.minTileSize || c.coord.height < this.cfg.minTileSize) {\r\n c.children = undefined;\r\n continue;\r\n }\r\n this.layoutNode(c, layout);\r\n }\r\n }\r\n\r\n private roundCoordsDeep(node: TreeNode<T>, decimals: number): void {\r\n if (node.dataItem) {\r\n node.coord = {\r\n width: roundN(node.coord.width, decimals),\r\n height: roundN(node.coord.height, decimals),\r\n top: roundN(node.coord.top, decimals),\r\n left: roundN(node.coord.left, decimals)\r\n };\r\n }\r\n if (node.children?.length) {\r\n for (const c of node.children) this.roundCoordsDeep(c, decimals);\r\n }\r\n }\r\n\r\n private buildPath(node: TreeNode<T>): T[] {\r\n if (!this.root) return [];\r\n const targetUid = node.uid;\r\n\r\n const stack: Array<{ n: TreeNode<T>; path: T[] }> = [{ n: this.root, path: [] }];\r\n while (stack.length) {\r\n const cur = stack.pop()!;\r\n const n = cur.n;\r\n\r\n if (n.uid === targetUid) return cur.path;\r\n\r\n const children = n.children ?? [];\r\n for (const c of children) {\r\n const nextPath = c.dataItem ? [...cur.path, c.dataItem] : cur.path;\r\n stack.push({ n: c, path: nextPath });\r\n }\r\n }\r\n return [];\r\n }\r\n\r\n private nextUid(): string {\r\n this.uidSeq += 1;\r\n return `tm_${this.uidSeq}`;\r\n }\r\n}\r\n\r\n","<div #host class=\"sc-treemap k-widget\">\r\n <ng-container *ngIf=\"root as r\">\r\n <div class=\"sc-treemap-root\">\r\n <!-- дети псевдо-корня -->\r\n <div class=\"sc-treemap-wrap sc-treemap-wrap-col\">\r\n <ng-container *ngFor=\"let child of r.children; trackBy: trackByUid\">\r\n <ng-container *ngTemplateOutlet=\"nodeTpl; context: { $implicit: child }\"></ng-container>\r\n </ng-container>\r\n </div>\r\n </div>\r\n </ng-container>\r\n</div>\r\n\r\n<ng-template #nodeTpl let-node>\r\n <!-- Leaf -->\r\n <ng-container *ngIf=\"isLeaf(node); else containerTpl\">\r\n <div\r\n class=\"sc-treemap-tile k-leaf\"\r\n [class.k-inverse]=\"isInverse(node)\"\r\n [class.k-hover]=\"hoveredUid === node.uid\"\r\n [style.left.px]=\"node.coord.left\"\r\n [style.top.px]=\"node.coord.top\"\r\n [style.width.px]=\"node.coord.width\"\r\n [style.height.px]=\"node.coord.height\"\r\n [style.backgroundColor]=\"node.color\"\r\n (mouseenter)=\"onTileMouseEnter(node)\"\r\n (mouseleave)=\"onTileMouseLeave(node)\"\r\n (click)=\"onTileClick(node)\"\r\n [attr.data-uid]=\"node.uid\"\r\n >\r\n <div class=\"sc-treemap-leaf-content\">\r\n <ng-container *ngIf=\"tileTemplate; else defaultLeaf\">\r\n <ng-container\r\n *ngTemplateOutlet=\"tileTemplate; context: { $implicit: node.dataItem, node: node, isLeaf: true }\">\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-template #defaultLeaf>\r\n <!-- дефолт под твой формат: name + ticker + percent -->\r\n <div class=\"leaf-meta\">{{ node.text }}</div>\r\n\r\n \r\n </ng-template>\r\n </div>\r\n </div>\r\n </ng-container>\r\n\r\n<!-- Container -->\r\n<ng-template #containerTpl>\r\n <div\r\n class=\"sc-treemap-tile k-node\"\r\n [style.left.px]=\"node.coord.left\"\r\n [style.top.px]=\"node.coord.top\"\r\n [style.width.px]=\"node.coord.width\"\r\n [style.height.px]=\"node.coord.height\"\r\n [attr.data-uid]=\"node.uid\"\r\n >\r\n <!-- TITLE: убираем из flow -->\r\n <ng-container *ngIf=\"titleSizeFor(node) as ts\">\r\n <div\r\n class=\"sc-treemap-title\"\r\n [class.sc-treemap-title-vertical]=\"isTitleSide()\"\r\n [class.tm-title-top]=\"!isTitleSide()\"\r\n [class.tm-title-left]=\"isTitleSide()\"\r\n [style.height.px]=\"!isTitleSide() ? ts : null\"\r\n [style.width.px]=\"isTitleSide() ? ts : null\"\r\n (mouseenter)=\"onTileMouseEnter(node)\"\r\n (mouseleave)=\"onTileMouseLeave(node)\"\r\n (click)=\"onTileClick(node)\"\r\n >\r\n <ng-container *ngIf=\"titleTemplate; else defaultTitle\">\r\n <ng-container\r\n *ngTemplateOutlet=\"titleTemplate; context: { $implicit: node.dataItem, node: node, isLeaf: false }\">\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-template #defaultTitle>\r\n <div class=\"sc-treemap-title-inner\">{{ node.text }}</div>\r\n </ng-template>\r\n </div>\r\n </ng-container>\r\n\r\n <!-- WRAP: занимает ровно оставшееся место -->\r\n <div\r\n class=\"sc-treemap-wrap sc-treemap-node-wrap\"\r\n [class.tm-wrap-top]=\"!isTitleSide()\"\r\n [class.tm-wrap-left]=\"isTitleSide()\"\r\n [style.top.px]=\"!isTitleSide() ? titleSizeFor(node) : 0\"\r\n [style.left.px]=\"isTitleSide() ? titleSizeFor(node) : 0\"\r\n >\r\n <ng-container *ngFor=\"let child of node.children; trackBy: trackByUid\">\r\n <ng-container *ngTemplateOutlet=\"nodeTpl; context: { $implicit: child }\"></ng-container>\r\n </ng-container>\r\n </div>\r\n </div>\r\n</ng-template>\r\n","/*\r\n * Public API Surface of stockchart-treemap\r\n */\r\n\r\nexport * from './lib/stockchart-treemap';\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;;;;;AAEA,MAAM,SAAS,GAAG,WAAW;AAEvB,SAAU,QAAQ,CAAC,IAAY,EAAE,GAAQ,EAAA;AAC7C,IAAA,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,SAAS;AAAE,QAAA,OAAO,GAAG;AACjD,IAAA,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;IAC3D,IAAI,GAAG,GAAG,GAAG;AACb,IAAA,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE;AACrB,QAAA,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,SAAS;AAAE,YAAA,OAAO,SAAS;AACvD,QAAA,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC;IACd;AACA,IAAA,OAAO,GAAG;AACZ;AAEM,SAAU,OAAO,CAAC,KAAU,EAAA;AAChC,IAAA,OAAO,OAAO,KAAK,KAAK,SAAS;AACnC;AAEM,SAAU,QAAQ,CAAC,CAAM,EAAA;AAC7B,IAAA,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,SAAS;AAAE,QAAA,OAAO,CAAC;IAC3C,MAAM,CAAC,GAAG,OAAO,CAAC,KAAK,QAAQ,GAAG,CAAC,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;AAC7E,IAAA,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC;AACnC;AAEM,SAAU,WAAW,CAAC,KAAiB,EAAA;IAC3C,IAAI,KAAK,GAAG,CAAC;AACb,IAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE;QAAE,KAAK,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC;AACpE,IAAA,OAAO,KAAK;AACd;AAEM,SAAU,KAAK,CAAC,KAAa,EAAA;IACjC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC;IAC7B,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,GAAG,KAAK;AAC1C;AAEM,SAAU,MAAM,CAAC,KAAa,EAAE,QAAgB,EAAA;IACpD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,QAAQ,CAAC;IACpC,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,GAAG,KAAK;AAC1C;SAEgB,cAAc,CAAC,GAAW,EAAE,GAAW,EAAE,MAAc,EAAA;AACrE,IAAA,MAAM,eAAe,GAAG,YAAY,CAAC,GAAG,CAAC;AACzC,IAAA,MAAM,eAAe,GAAG,YAAY,CAAC,GAAG,CAAC;AACzC,IAAA,MAAM,QAAQ,GAAG,eAAe,CAAC,GAAG,CAAC,GAAG,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC;IAChE,MAAM,MAAM,GAAa,EAAE;AAE3B,IAAA,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;AAEhB,IAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,EAAE,EAAE;AAC/B,QAAA,MAAM,QAAQ,GAAG;AACf,YAAA,CAAC,EAAE,YAAY,CAAC,eAAe,CAAC,CAAC,EAAE,eAAe,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC;AAC1E,YAAA,CAAC,EAAE,YAAY,CAAC,eAAe,CAAC,CAAC,EAAE,eAAe,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC;AAC1E,YAAA,CAAC,EAAE,YAAY,CAAC,eAAe,CAAC,CAAC,EAAE,eAAe,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ;SAC1E;QACD,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IAC1C;AAEA,IAAA,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;AAChB,IAAA,OAAO,MAAM;AACf;AAEM,SAAU,mBAAmB,CAAC,KAAa,EAAE,GAAW,EAAE,GAAW,EAAE,KAAwB,EAAA;IACnG,MAAM,SAAS,GAAG,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ;IAClD,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC;IACrC,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC;AAErC,IAAA,IAAI,UAAU,KAAK,UAAU,EAAE;QAC7B,OAAO,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,KAAK,CAAC,GAAG;IAC/C;IAEA,IAAI,SAAS,IAAI,UAAU,GAAG,CAAC,IAAI,UAAU,GAAG,CAAC,EAAE;AACjD,QAAA,IAAI,KAAK,IAAI,CAAC,EAAE;AACd,YAAA,MAAM,KAAK,GAAG,UAAU,KAAK,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,KAAK,GAAG,UAAU,CAAC;AAChE,YAAA,OAAO,gBAAgB,CAAC,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC;QACtE;AACA,QAAA,MAAM,KAAK,GAAG,UAAU,KAAK,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,KAAK,GAAG,UAAU,CAAC;AAChE,QAAA,OAAO,gBAAgB,CAAC,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC;IACtE;AAEA,IAAA,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,KAAK,GAAG,UAAU,KAAK,UAAU,GAAG,UAAU,CAAC,CAAC;AACvE,IAAA,OAAO,gBAAgB,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC;AACtD;AAEA,SAAS,YAAY,CAAC,GAAW,EAAE,GAAW,EAAE,KAAa,EAAE,MAAc,EAAE,QAAiB,EAAA;IAC9F,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACvD,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AACvD,IAAA,MAAM,IAAI,GAAG,CAAC,QAAQ,GAAG,QAAQ,KAAK,MAAM,GAAG,CAAC,CAAC;IACjD,MAAM,WAAW,GAAG,IAAI,IAAI,KAAK,GAAG,CAAC,CAAC;AAEtC,IAAA,OAAO,QAAQ,IAAI,QAAQ,GAAG,WAAW,KAAK,QAAQ,GAAG,WAAW,CAAC;AACvE;AAEA,SAAS,gBAAgB,CAAC,GAAW,EAAE,GAAW,EAAE,KAAa,EAAA;AAC/D,IAAA,MAAM,KAAK,GAAG,YAAY,CAAC,GAAG,CAAC;AAC/B,IAAA,MAAM,GAAG,GAAG,YAAY,CAAC,GAAG,CAAC;AAC7B,IAAA,MAAM,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC;AAExB,IAAA,OAAO,iBAAiB,CAAC;AACvB,QAAA,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;AAC1B,QAAA,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;AAC1B,QAAA,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC;AAC1B,KAAA,CAAC;AACJ;AAEA,SAAS,iBAAiB,CAAC,KAA0C,EAAA;IACnE,OAAO,GAAG,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC;AACpF;AAEA,SAAS,IAAI,CAAC,IAAY,EAAE,EAAU,EAAE,CAAS,EAAA;IAC/C,OAAO,IAAI,GAAG,CAAC,EAAE,GAAG,IAAI,IAAI,CAAC;AAC/B;AAEA,SAAS,YAAY,CAAC,KAAa,EAAA;IACjC,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC;AAC9B,IAAA,MAAM,QAAQ,GAAG,UAAU,CAAC,KAAK,CAAC;IAElC,OAAO;AACL,QAAA,CAAC,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;AACvB,QAAA,CAAC,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;AACvB,QAAA,CAAC,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC;KACvB;AACH;AAEA,SAAS,YAAY,CAAC,MAAc,EAAA;AAClC,IAAA,IAAI,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE;AAC1D,IAAA,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;AAAE,QAAA,MAAM,GAAG,GAAG,GAAG,MAAM;AAC9C,IAAA,OAAO,MAAM;AACf;AAEA,SAAS,UAAU,CAAC,KAAa,EAAA;AAC/B,IAAA,MAAM,WAAW,GAAG,KAAK,CAAC,MAAM;IAChC,MAAM,QAAQ,GAAQ,EAAE;AACxB,IAAA,IAAI,WAAW,KAAK,CAAC,EAAE;AACrB,QAAA,QAAQ,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC;AACrB,QAAA,QAAQ,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC;AACrB,QAAA,QAAQ,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC;IACvB;SAAO;QACL,QAAQ,CAAC,CAAC,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC;QAClC,QAAQ,CAAC,CAAC,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC;QAClC,QAAQ,CAAC,CAAC,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC;IACpC;AACA,IAAA,OAAO,QAAQ;AACjB;AAEA,SAAS,QAAQ,CAAC,GAAW,EAAA;IAC3B,OAAO,QAAQ,CAAE,GAAW,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;AAChD;AAEM,SAAU,eAAe,CAAC,KAAc,EAAA;IAC5C,IAAI,UAAU,GAAG,CAAC;IAClB,IAAI,KAAK,EAAE;AACT,QAAA,MAAM,CAAC,GAAG,YAAY,CAAC,KAAK,CAAC;AAC7B,QAAA,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IACnF;AACA,IAAA,OAAO,UAAU;AACnB;AAEA,SAAS,OAAO,CAAC,KAAa,EAAA;AAC5B,IAAA,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;AAAE,QAAA,OAAO,CAAC;AACrC,IAAA,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;AACxC;;AC9JA,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS;MAErB,gBAAgB,CAAA;AAA7B,IAAA,WAAA,GAAA;QACU,IAAA,CAAA,WAAW,GAAc,GAAG;IA6JtC;IA3JE,OAAO,CAAC,QAAoB,EAAE,KAAY,EAAA;AACxC,QAAA,IAAI,EAAE,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC,EAAE;YAC7D,IAAI,CAAC,YAAY,EAAE;QACrB;AACA,QAAA,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC;AAAE,YAAA,IAAI,CAAC,cAAc,CAAC,QAAQ,EAAE,KAAK,CAAC;IAC/D;IAEQ,cAAc,CAAC,KAAiB,EAAE,KAAY,EAAA;QACpD,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,MAAM;QAC7C,IAAI,SAAS,GAAG,CAAC;QACjB,MAAM,SAAS,GAAa,EAAE;AAE9B,QAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;AACrC,YAAA,SAAS,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC;AACtD,YAAA,SAAS,IAAI,SAAS,CAAC,CAAC,CAAC;QAC3B;QACA,IAAI,SAAS,IAAI,CAAC;YAAE;AAEpB,QAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;AACzC,YAAA,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,UAAU,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS;QACvD;AAEA,QAAA,MAAM,gBAAgB,GAAG,IAAI,CAAC,gBAAgB,EAAE,GAAG,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,KAAK;QAE7E,MAAM,YAAY,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC/B,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;QAC3B,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,YAAY,EAAE,gBAAgB,EAAE,KAAK,CAAC;IAC5D;AAEQ,IAAA,QAAQ,CAAC,IAAgB,EAAE,WAAuB,EAAE,KAAa,EAAE,KAAY,EAAA;QACrF,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK,CAAC;IAClD;AAEQ,IAAA,UAAU,CAAC,IAAgB,EAAE,WAAuB,EAAE,KAAa,EAAE,KAAY,EAAA;QACvF,IAAI,IAAI,CAAC,MAAM,GAAG,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE;AAC1C,YAAA,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,KAAK,CAAC,GAAG,IAAI,GAAG,WAAW;AACtD,YAAA,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,KAAK,CAAC;YAC/B;QACF;AAEA,QAAA,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE;AAChD,YAAA,WAAW,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACvB,YAAA,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QACtB;AAEA,QAAA,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE;AACrB,YAAA,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC;gBAAE,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,KAAK,EAAE,KAAK,CAAC;YACrE;QACF;AAEA,QAAA,MAAM,YAAY,GAAG,IAAI,CAAC,CAAC,CAAC;QAE5B,IAAI,IAAI,CAAC,gBAAgB,CAAC,WAAW,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,gBAAgB,CAAC,CAAC,YAAY,EAAE,GAAG,WAAW,CAAC,EAAE,KAAK,CAAC,EAAE;YAC7G,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,WAAW,EAAE,YAAY,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC;QAC9E;aAAO;AACL,YAAA,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,KAAK,EAAE,KAAK,CAAC;AAC3D,YAAA,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,EAAE,SAAS,CAAC,GAAG,EAAE,SAAS,CAAC;QACrD;IACF;IAEQ,UAAU,CAAC,KAAiB,EAAE,KAAY,EAAA;AAChD,QAAA,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,KAAK;IACxB;AAEQ,IAAA,SAAS,CAAC,KAAiB,EAAE,KAAa,EAAE,KAAY,EAAA;AAC9D,QAAA,OAAO,IAAI,CAAC,gBAAgB,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC;IACxG;IAEQ,cAAc,GAAA,EAAc,OAAO,IAAI,CAAC,WAAW,KAAK,GAAG,CAAC,CAAC;IAC7D,gBAAgB,GAAA,EAAc,OAAO,IAAI,CAAC,WAAW,KAAK,GAAG,CAAC,CAAC;IAE/D,YAAY,GAAA;AAClB,QAAA,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,cAAc,EAAE,GAAG,GAAG,GAAG,GAAG;IACtD;IAEQ,gBAAgB,CAAC,KAAiB,EAAE,KAAa,EAAA;AACvD,QAAA,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;AAAE,YAAA,OAAO,SAAS;QAElD,IAAI,OAAO,GAAG,CAAC;QACf,IAAI,OAAO,GAAG,CAAC;QACf,IAAI,OAAO,GAAG,SAAS;AAEvB,QAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;YACrC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;YAC/B,OAAO,IAAI,IAAI;AACf,YAAA,OAAO,GAAG,CAAC,OAAO,GAAG,IAAI,IAAI,OAAO,GAAG,IAAI;AAC3C,YAAA,OAAO,GAAG,CAAC,OAAO,GAAG,IAAI,IAAI,OAAO,GAAG,IAAI;QAC7C;QACA,IAAI,OAAO,IAAI,CAAC;AAAE,YAAA,OAAO,SAAS;AAElC,QAAA,OAAO,IAAI,CAAC,GAAG,CACb,CAAC,KAAK,GAAG,KAAK,GAAG,OAAO,KAAK,OAAO,GAAG,OAAO,CAAC,EAC/C,CAAC,OAAO,GAAG,OAAO,KAAK,KAAK,GAAG,KAAK,GAAG,OAAO,CAAC,CAChD;IACH;AAEQ,IAAA,OAAO,CAAC,KAAiB,EAAE,KAAa,EAAE,KAAY,EAAA;AAC5D,QAAA,MAAM,SAAS,GAAG,WAAW,CAAC,KAAK,CAAC;QACpC,IAAI,GAAG,GAAG,CAAC;AAEX,QAAA,KAAK,GAAG,KAAK,CAAC,SAAS,GAAG,KAAK,CAAC;AAEhC,QAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;AACrC,YAAA,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,KAAK,CAAC;AAClD,YAAA,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG;gBACf,MAAM;gBACN,KAAK;AACL,gBAAA,GAAG,EAAE,KAAK,CAAC,GAAG,GAAG,GAAG;gBACpB,IAAI,EAAE,KAAK,CAAC;aACb;YACD,GAAG,IAAI,MAAM;QACf;AAEA,QAAA,MAAM,GAAG,GAA4B;YACnC,MAAM,EAAE,KAAK,CAAC,MAAM;AACpB,YAAA,KAAK,EAAE,KAAK,CAAC,KAAK,GAAG,KAAK;YAC1B,GAAG,EAAE,KAAK,CAAC,GAAG;AACd,YAAA,IAAI,EAAE,KAAK,CAAC,IAAI,GAAG,KAAK;AACxB,YAAA,GAAG,EAAE;SACN;AAED,QAAA,GAAG,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC;AACzC,QAAA,IAAI,GAAG,CAAC,GAAG,KAAK,GAAG,CAAC,MAAM;YAAE,IAAI,CAAC,YAAY,EAAE;AAC/C,QAAA,OAAO,GAAG;IACZ;AAEQ,IAAA,OAAO,CAAC,KAAiB,EAAE,KAAa,EAAE,KAAY,EAAA;AAC5D,QAAA,MAAM,SAAS,GAAG,WAAW,CAAC,KAAK,CAAC;QAEpC,MAAM,MAAM,GAAG,KAAK,CAAC,SAAS,GAAG,KAAK,CAAC;AACvC,QAAA,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG;QACrB,IAAI,IAAI,GAAG,CAAC;AAEZ,QAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;AACrC,YAAA,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG;gBACf,MAAM;AACN,gBAAA,KAAK,EAAE,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,MAAM,CAAC;gBAC3C,GAAG;AACH,gBAAA,IAAI,EAAE,KAAK,CAAC,IAAI,GAAG;aACpB;YACD,IAAI,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK;QAC9B;AAEA,QAAA,MAAM,GAAG,GAA4B;AACnC,YAAA,MAAM,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM;YAC7B,KAAK,EAAE,KAAK,CAAC,KAAK;AAClB,YAAA,GAAG,EAAE,KAAK,CAAC,GAAG,GAAG,MAAM;YACvB,IAAI,EAAE,KAAK,CAAC,IAAI;AAChB,YAAA,GAAG,EAAE;SACN;AAED,QAAA,GAAG,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC;AACzC,QAAA,IAAI,GAAG,CAAC,GAAG,KAAK,GAAG,CAAC,KAAK;YAAE,IAAI,CAAC,YAAY,EAAE;AAC9C,QAAA,OAAO,GAAG;IACZ;AACD;MAEY,kBAAkB,CAAA;AAI7B,IAAA,WAAA,CAAY,QAAiB,EAAA;AAC3B,QAAA,IAAI,CAAC,QAAQ,GAAG,QAAQ;AACxB,QAAA,IAAI,CAAC,QAAQ,GAAG,QAAQ,GAAG,CAAC,GAAG,CAAC;IAClC;IAEA,OAAO,CAAC,QAAoB,EAAE,KAAY,EAAA;AACxC,QAAA,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;YAAE;QAE3B,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,MAAM;QAC7C,IAAI,SAAS,GAAG,CAAC;QACjB,MAAM,SAAS,GAAa,EAAE;AAE9B,QAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;AACxC,YAAA,SAAS,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC;AACzD,YAAA,SAAS,IAAI,SAAS,CAAC,CAAC,CAAC;YACzB,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ;QACtC;QACA,IAAI,SAAS,IAAI,CAAC;YAAE;AAEpB,QAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;AACzC,YAAA,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,UAAU,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS;QAC1D;AAEA,QAAA,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE,KAAK,CAAC;IACpC;IAEQ,YAAY,CAAC,KAAiB,EAAE,KAAY,EAAA;AAClD,QAAA,MAAM,SAAS,GAAG,WAAW,CAAC,KAAK,CAAC;AACpC,QAAA,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,MAAM,IAAI,CAAC,QAAQ,EAAE;YAC1C,IAAI,CAAC,gBAAgB,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,CAAC;QAChD;aAAO;YACL,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,CAAC;QAC9C;IACF;AAEQ,IAAA,gBAAgB,CAAC,KAAiB,EAAE,KAAY,EAAE,SAAiB,EAAA;QACzE,IAAI,IAAI,GAAG,CAAC;AAEZ,QAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;AACrC,YAAA,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC;AACrB,YAAA,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC;YAE1D,IAAI,CAAC,KAAK,GAAG;gBACX,MAAM,EAAE,KAAK,CAAC,MAAM;gBACpB,KAAK;gBACL,GAAG,EAAE,KAAK,CAAC,GAAG;AACd,gBAAA,IAAI,EAAE,KAAK,CAAC,IAAI,GAAG;aACpB;YACD,IAAI,IAAI,KAAK;QACf;IACF;AAEQ,IAAA,cAAc,CAAC,KAAiB,EAAE,KAAY,EAAE,SAAiB,EAAA;QACvE,IAAI,GAAG,GAAG,CAAC;AAEX,QAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;AACrC,YAAA,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC;AACrB,YAAA,MAAM,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,SAAS,GAAG,KAAK,CAAC,MAAM,CAAC;YAE5D,IAAI,CAAC,KAAK,GAAG;gBACX,MAAM;gBACN,KAAK,EAAE,KAAK,CAAC,KAAK;AAClB,gBAAA,GAAG,EAAE,KAAK,CAAC,GAAG,GAAG,GAAG;gBACpB,IAAI,EAAE,KAAK,CAAC;aACb;YACD,GAAG,IAAI,MAAM;QACf;IACF;AACD;;MC7MY,gBAAgB,CAAA;AAiC3B,IAAA,IAAY,GAAG,GAAA;QACb,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,IAAI,OAAO;QACrD,MAAM,eAAe,GAAG,IAAI,CAAC,OAAO,CAAC,eAAe,IAAI,UAAU;QAElE,OAAO;AACL,YAAA,IAAI,EAAE,YAAY;AAClB,YAAA,SAAS,EAAE,MAAM;YACjB,UAAU;AACV,YAAA,UAAU,EAAE,OAAO;YACnB,eAAe;AACf,YAAA,aAAa,EAAE,OAAO;AACtB,YAAA,MAAM,EAAE,CAAC,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,CAAC;AAC1E,YAAA,UAAU,EAAE,SAAS;AACrB,YAAA,SAAS,EAAE,EAAE;AACb,YAAA,kBAAkB,EAAE,IAAI;AACxB,YAAA,6BAA6B,EAAE,IAAI;AACnC,YAAA,aAAa,EAAE,CAAC;AAChB,YAAA,WAAW,EAAE,CAAC;YACd,GAAG,IAAI,CAAC;SACT;IACH;IAGA,WAAA,CAAoB,IAAY,EAAU,GAAsB,EAAA;QAA5C,IAAA,CAAA,IAAI,GAAJ,IAAI;QAAkB,IAAA,CAAA,GAAG,GAAH,GAAG;;QApDpC,IAAA,CAAA,IAAI,GAAe,IAAI;AAOvB,QAAA,IAAA,CAAA,SAAS,GAAkB,IAAI,CAAC;QAEhC,IAAA,CAAA,OAAO,GAA4B,EAAE;AAKpC,QAAA,IAAA,CAAA,SAAS,GAAG,IAAI,YAAY,EAAQ;AACpC,QAAA,IAAA,CAAA,SAAS,GAAG,IAAI,YAAY,EAAmB;AAC/C,QAAA,IAAA,CAAA,SAAS,GAAG,IAAI,YAAY,EAAmB;QAEzD,IAAA,CAAA,IAAI,GAAuB,IAAI;QAC/B,IAAA,CAAA,UAAU,GAAkB,IAAI;QAExB,IAAA,CAAA,EAAE,GAA0B,IAAI;QAChC,IAAA,CAAA,aAAa,GAAG,KAAK;QAErB,IAAA,CAAA,MAAM,GAAG,CAAC;QACV,IAAA,CAAA,QAAQ,GAAG,CAAC;QAEZ,IAAA,CAAA,SAAS,GAAwB,IAAI;QAoE7C,IAAA,CAAA,UAAU,GAAG,CAAC,CAAS,EAAE,CAAc,KAAK,CAAC,CAAC,GAAG;IA3CkB;IAGnE,eAAe,GAAA;AAEb,QAAA,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,MAAK;AAC/B,YAAA,IAAI,CAAC,EAAE,GAAG,IAAI,cAAc,CAAC,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC;YACvD,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC;AAC1C,QAAA,CAAC,CAAC;QAEF,IAAI,CAAC,eAAe,EAAE;AACtB,QAAA,IAAI,CAAC,OAAO,EAAE,CAAC;IACjB;AAEA,IAAA,WAAW,CAAC,OAAsB,EAAA;AAChC,QAAA,IAAI,OAAO,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,IAAI,OAAO,CAAC,WAAW,CAAC,EAAE;YACjE,IAAI,CAAC,eAAe,EAAE;QACxB;QACA,IAAI,OAAO,CAAC,MAAM,CAAC,IAAI,OAAO,CAAC,SAAS,CAAC,EAAE;YACzC,IAAI,CAAC,YAAY,EAAE;QACrB;IACF;IAEA,WAAW,GAAA;AACT,QAAA,IAAI,CAAC,EAAE,EAAE,UAAU,EAAE;AACrB,QAAA,IAAI,CAAC,EAAE,GAAG,IAAI;AACd,QAAA,IAAI,CAAC,SAAS,EAAE,WAAW,EAAE;AAC7B,QAAA,IAAI,CAAC,SAAS,GAAG,IAAI;IACvB;;AAGA,IAAA,MAAM,CAAC,IAAiB,EAAA;AACtB,QAAA,OAAO,EAAE,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;IACjD;AAEA,IAAA,SAAS,CAAC,IAAiB,EAAA;QACzB,OAAO,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG;IAC1C;IAEA,WAAW,GAAA;AACT,QAAA,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,KAAK,YAAY;IACvC;AAIA,IAAA,YAAY,CAAC,IAAiB,EAAA;;QAE5B,IAAI,CAAC,IAAI,CAAC,QAAQ;AAAE,YAAA,OAAO,CAAC;QAC5B,MAAM,WAAW,GAAG,CAAC,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM;AAC3C,QAAA,IAAI,CAAC,WAAW;AAAE,YAAA,OAAO,CAAC;;QAE1B,IAAI,IAAI,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,kBAAkB;AAAE,YAAA,OAAO,CAAC;AAC9D,QAAA,MAAM,OAAO,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC;AACnD,QAAA,MAAM,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,aAAa;AACnC,QAAA,IAAI,CAAC,OAAO,IAAI,CAAC,MAAM;AAAE,YAAA,OAAO,CAAC;AACjC,QAAA,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC;IACxC;AAEF,IAAA,gBAAgB,CAAC,IAAiB,EAAA;QAChC,IAAI,CAAC,IAAI,CAAC,QAAQ;YAAE;AACpB,QAAA,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,GAAG;QAC1B,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;IACpF;AAEA,IAAA,gBAAgB,CAAC,IAAiB,EAAA;AAChC,QAAA,IAAI,IAAI,CAAC,UAAU,KAAK,IAAI,CAAC,GAAG;AAAE,YAAA,IAAI,CAAC,UAAU,GAAG,IAAI;IAC1D;AAEA,IAAA,WAAW,CAAC,IAAiB,EAAA;QAC3B,IAAI,CAAC,IAAI,CAAC,QAAQ;YAAE;QACpB,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;IACpF;;IAIE,UAAU,GAAA;;;QAGR,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE;YAC9B,IAAI,CAAC,eAAe,EAAE;YACtB;QACF;QACA,IAAI,CAAC,YAAY,EAAE;IACrB;;IAGQ,eAAe,GAAA;AAErB,QAAA,IAAI,CAAC,SAAS,EAAE,WAAW,EAAE;AAC7B,QAAA,IAAI,CAAC,SAAS,GAAG,IAAI;;AAGrB,QAAA,IAAI,IAAI,CAAC,KAAK,EAAE;YACd,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,GAAG,IAAG;AAC1C,gBAAA,IAAI,CAAC,IAAI,GAAG,GAAG,IAAI,EAAE;gBACrB,IAAI,CAAC,YAAY,EAAE;AACrB,YAAA,CAAC,CAAC;YACF;QACF;AAEA,QAAA,IAAI,IAAI,CAAC,MAAM,EAAE;AACf,YAAA,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAK;AAC1B,gBAAA,MAAM,GAAG,GAAG,IAAI,CAAC,MAAO,EAAE;gBAC1B,IAAI,YAAY,CAAC,GAAG,CAAC;AAAE,oBAAA,OAAO,GAAG;gBACjC,IAAI,GAAG,YAAY,OAAO;AAAE,oBAAA,OAAO,IAAI,CAAC,GAAG,CAAC;AAC5C,gBAAA,OAAO,EAAE,CAAC,GAAU,CAAC;AACvB,YAAA,CAAC,CAAC;YAEF,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,SAAS,GAAG,CAAC;YACjD,MAAM,OAAO,GAAG;AACd,kBAAE,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,SAAU,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,QAAQ,CAAC;kBACxD,QAAQ;YAEZ,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,GAAG,IAAG;AAC9C,gBAAA,IAAI,CAAC,IAAI,GAAG,GAAG,IAAI,EAAE;AACtB,gBAAA,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE;gBACvB,IAAI,CAAC,YAAY,EAAE;AACb,YAAA,CAAC,CAAC;QACJ;IACF;;IAGQ,YAAY,GAAA;QAClB,IAAI,IAAI,CAAC,aAAa;YAAE;AACxB,QAAA,IAAI,CAAC,aAAa,GAAG,IAAI;QAEzB,cAAc,CAAC,MAAK;AAClB,YAAA,IAAI,CAAC,aAAa,GAAG,KAAK;YAC1B,IAAI,CAAC,OAAO,EAAE;AAChB,QAAA,CAAC,CAAC;IACJ;IAEQ,OAAO,GAAA;AAEb,QAAA,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,EAAE,aAAa;AACnC,QAAA,IAAI,CAAC,EAAE;YAAE;AAET,QAAA,MAAM,IAAI,GAAG,EAAE,CAAC,qBAAqB,EAAE;AACvC,QAAA,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC;AACrC,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC;AACvC,QAAA,IAAI,KAAK,KAAK,CAAC,IAAI,MAAM,KAAK,CAAC;YAAE;AAEjC,QAAA,IAAI,CAAC,MAAM,GAAG,CAAC;AACf,QAAA,IAAI,CAAC,QAAQ,GAAG,CAAC;AAEjB,QAAA,MAAM,IAAI,GAAgB;AACxB,YAAA,GAAG,EAAE,IAAI,CAAC,OAAO,EAAE;AACnB,YAAA,KAAK,EAAE,CAAC;AACR,YAAA,IAAI,EAAE,EAAE;AACR,YAAA,KAAK,EAAE,CAAC;AACR,YAAA,KAAK,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE;AACzC,YAAA,QAAQ,EAAE,IAAI;AACd,YAAA,QAAQ,EAAE,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC;SAC7C;AAED,QAAA,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;AAEnB,QAAA,IAAI,IAAI,CAAC,GAAG,CAAC,6BAA6B,EAAE;AAC1C,YAAA,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC;QACzB;AAEA,QAAA,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;AAEtB,QAAA,MAAM,MAAM,GACV,IAAI,CAAC,GAAG,CAAC,IAAI,KAAK,UAAU,GAAG,IAAI,kBAAkB,CAAC,IAAI,CAAC;AAC3D,YAAA,IAAI,CAAC,GAAG,CAAC,IAAI,KAAK,YAAY,GAAG,IAAI,kBAAkB,CAAC,KAAK,CAAC;gBAC9D,IAAI,gBAAgB,EAAE;AAExB,QAAA,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC;;AAG7B,QAAA,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,aAAa;AAChC,QAAA,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,CAAC,CAAC;AAE7B,QAAA,IAAI,CAAC,IAAI,GAAG,IAAI;AACpB,QAAA,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;AAEpB,QAAA,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;IAC5C;IAEQ,UAAU,CAAC,KAAU,EAAE,KAAa,EAAA;AAE1C,QAAA,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,eAAe,EAAE,aAAa,EAAE,GAAG,IAAI,CAAC,GAAG;QAEtF,MAAM,KAAK,GAAkB,EAAE;AAC/B,QAAA,KAAK,MAAM,IAAI,IAAI,KAAK,IAAI,EAAE,EAAE;YAC9B,MAAM,OAAO,GAAG,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC;YACzC,MAAM,QAAQ,GAAG,QAAQ,CAAC,UAAU,EAAE,IAAI,CAAC;YAC3C,MAAM,QAAQ,GAAG,QAAQ,CAAC,UAAU,EAAE,IAAI,CAAC;YAC3C,MAAM,aAAa,GAAG,QAAQ,CAAC,eAAe,EAAE,IAAI,CAAC;YACrD,MAAM,WAAW,GAAG,QAAQ,CAAC,aAAa,EAAE,IAAI,CAAC;AAEjD,YAAA,MAAM,WAAW,GAAG,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,GAAI,WAAmB,GAAG,EAAE;AAE1E,YAAA,MAAM,IAAI,GAAgB;AACxB,gBAAA,GAAG,EAAE,IAAI,CAAC,OAAO,EAAE;gBACnB,KAAK;AACL,gBAAA,IAAI,EAAE,CAAC,OAAO,IAAI,EAAE,IAAI,EAAE;AAC1B,gBAAA,KAAK,EAAE,QAAQ,CAAC,QAAQ,CAAC;AACzB,gBAAA,UAAU,EAAE,QAAQ,CAAC,aAAa,CAAC;AACnC,gBAAA,KAAK,EAAE,OAAO,QAAQ,KAAK,QAAQ,GAAG,QAAQ,GAAG,SAAS;AAC1D,gBAAA,KAAK,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE;AAC/C,gBAAA,QAAQ,EAAE,IAAI;gBAChB,QAAQ,EAAE,WAAW,CAAC,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,WAAW,EAAE,KAAK,GAAG,CAAC,CAAC,GAAG;aAC1E;;;AAIC,YAAA,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM;gBAAE;AAE5E,YAAA,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;QAClB;AAEA,QAAA,OAAO,KAAK;IACd;AAEQ,IAAA,QAAQ,CAAC,IAAiB,EAAA;AAChC,QAAA,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM;YAAE;AAC5B,QAAA,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC;AAC7D,QAAA,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ;AAAE,YAAA,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;IACjD;AAEQ,IAAA,YAAY,CAAC,IAAiB,EAAA;AACpC,QAAA,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE;AAC1B,YAAA,OAAO,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,KAAK,GAAG,CAAC;QACrD;QACA,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,KAAK,GAAG,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;AAC3E,QAAA,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,KAAK,CAAC;AAAE,YAAA,IAAI,CAAC,KAAK,GAAG,GAAG;QACtE,OAAO,IAAI,CAAC,KAAK;IACnB;AAEQ,IAAA,WAAW,CAAC,IAAiB,EAAA;AACnC,QAAA,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ;QAC3B,IAAI,CAAC,KAAK,EAAE,MAAM;YAAE;AAEpB,QAAA,IAAI,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE;YACvB,IAAI,CAAC,oBAAoB,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC;QACvD;AAEA,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM;AAC9B,QAAA,IAAI,MAAM,CAAC,MAAM,EAAE;AACjB,YAAA,IAAI,CAAC,kBAAkB,CAAC,KAAK,EAAE,MAAM,CAAC;QACxC;aAAO;AACL,YAAA,IAAI,CAAC,mBAAmB,CAAC,KAAK,CAAC;QACjC;QAEA,KAAK,MAAM,CAAC,IAAI,KAAK;AAAE,YAAA,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC;IAC5C;IAEQ,oBAAoB,CAAC,KAAoB,EAAE,KAAgD,EAAA;AACjG,QAAA,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;QACxD,IAAI,CAAC,MAAM,CAAC,MAAM;YAAE;QAEpB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC;QAC/B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC;AAE/B,QAAA,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;AACzD,QAAA,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE;YACxB,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE;gBACrB,MAAM,UAAU,GAAG,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,UAAW,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC;AACvE,gBAAA,CAAC,CAAC,KAAK,GAAG,mBAAmB,CAAC,UAAU,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,CAAC;YAC5D;QACF;IACF;IAEQ,kBAAkB,CAAC,KAAoB,EAAE,MAAwC,EAAA;QACvF,IAAI,CAAC,KAAK,CAAC,MAAM;YAAE;AAEnB,QAAA,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC;QACpD,IAAI,UAAU,GAAoB,IAAI;;;AAItC,QAAA,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE;AACzB,YAAA,UAAU,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC;QACjE;QAEA,IAAI,SAAS,GAAG,KAAK;AACrB,QAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;AACrC,YAAA,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC;YAErB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE;AACxB,gBAAA,IAAI,CAAC,KAAK,GAAG,UAAU,GAAG,UAAU,CAAC,CAAC,CAAC,GAAI,MAAiB;YAC9D;AAEA,YAAA,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM;gBAAE,SAAS,GAAG,IAAI;QAC9C;AAEA,QAAA,IAAI,SAAS;YAAE,IAAI,CAAC,QAAQ,EAAE;IAChC;AAEQ,IAAA,mBAAmB,CAAC,KAAoB,EAAA;AAC9C,QAAA,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE;AACrB,YAAA,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;AAAE,gBAAA,CAAC,CAAC,KAAK,GAAG,SAAS;QAC5C;IACF;AAEQ,IAAA,YAAY,CAAC,IAAiB,EAAA;AACpC,QAAA,MAAM,GAAG,GAAkB,CAAC,IAAI,CAAC;AACjC,QAAA,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,IAAI,EAAE;YAAE,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;AACtE,QAAA,OAAO,GAAG;IACZ;AAEQ,IAAA,aAAa,CAAC,IAAiB,EAAA;QACrC,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC,UAAW,GAAG,IAAI,CAAC,KAAK,IAAI,CAAC;AAC3E,QAAA,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM;YAAE,OAAO,CAAC,KAAK,CAAC;QAC1C,OAAO,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,IAAI,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;IACtE;IAEQ,UAAU,CAAC,IAAiB,EAAE,MAAc,EAAA;AAClD,QAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE;QAC/F,IAAI,CAAC,QAAQ,CAAC,MAAM;YAAE;QAEtB,MAAM,KAAK,GAAG,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC;AACrC,QAAA,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI;AAEvB,QAAA,MAAM,OAAO,GACX,CAAC,CAAC,KAAK,YAAY;AACjB,cAAE,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC;AAC5F,cAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE;QAElG,IAAI,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,WAAW,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE;AACjF,YAAA,IAAI,CAAC,QAAQ,GAAG,SAAS;YACzB;QACF;;AAGA,QAAA,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC;;AAGjC,QAAA,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE;YACxB,IAAI,CAAC,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,WAAW,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE;AACjF,gBAAA,CAAC,CAAC,QAAQ,GAAG,SAAS;gBACtB;YACF;AACA,YAAA,IAAI,CAAC,UAAU,CAAC,CAAC,EAAE,MAAM,CAAC;QAC5B;IACF;IAEQ,eAAe,CAAC,IAAiB,EAAE,QAAgB,EAAA;AACzD,QAAA,IAAI,IAAI,CAAC,QAAQ,EAAE;YACjB,IAAI,CAAC,KAAK,GAAG;gBACX,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,QAAQ,CAAC;gBACzC,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC;gBAC3C,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,QAAQ,CAAC;gBACrC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,QAAQ;aACvC;QACH;AACA,QAAA,IAAI,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE;AACzB,YAAA,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ;AAAE,gBAAA,IAAI,CAAC,eAAe,CAAC,CAAC,EAAE,QAAQ,CAAC;QAClE;IACF;AAEQ,IAAA,SAAS,CAAC,IAAiB,EAAA;QACjC,IAAI,CAAC,IAAI,CAAC,IAAI;AAAE,YAAA,OAAO,EAAE;AACzB,QAAA,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG;AAE1B,QAAA,MAAM,KAAK,GAAyC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;AAChF,QAAA,OAAO,KAAK,CAAC,MAAM,EAAE;AACnB,YAAA,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,EAAG;AACxB,YAAA,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC;AAEf,YAAA,IAAI,CAAC,CAAC,GAAG,KAAK,SAAS;gBAAE,OAAO,GAAG,CAAC,IAAI;AAExC,YAAA,MAAM,QAAQ,GAAG,CAAC,CAAC,QAAQ,IAAI,EAAE;AACjC,YAAA,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE;gBACxB,MAAM,QAAQ,GAAG,CAAC,CAAC,QAAQ,GAAG,CAAC,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,GAAG,GAAG,CAAC,IAAI;AAClE,gBAAA,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;YACtC;QACF;AACA,QAAA,OAAO,EAAE;IACX;IAEQ,OAAO,GAAA;AACb,QAAA,IAAI,CAAC,MAAM,IAAI,CAAC;AAChB,QAAA,OAAO,CAAA,GAAA,EAAM,IAAI,CAAC,MAAM,EAAE;IAC5B;+GAhbW,gBAAgB,EAAA,IAAA,EAAA,CAAA,EAAA,KAAA,EAAA,EAAA,CAAA,MAAA,EAAA,EAAA,EAAA,KAAA,EAAA,EAAA,CAAA,iBAAA,EAAA,CAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA,CAAA;mGAAhB,gBAAgB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,oBAAA,EAAA,MAAA,EAAA,EAAA,IAAA,EAAA,MAAA,EAAA,KAAA,EAAA,OAAA,EAAA,MAAA,EAAA,QAAA,EAAA,SAAA,EAAA,WAAA,EAAA,OAAA,EAAA,SAAA,EAAA,YAAA,EAAA,cAAA,EAAA,aAAA,EAAA,eAAA,EAAA,EAAA,OAAA,EAAA,EAAA,SAAA,EAAA,WAAA,EAAA,SAAA,EAAA,WAAA,EAAA,SAAA,EAAA,WAAA,EAAA,EAAA,WAAA,EAAA,CAAA,EAAA,YAAA,EAAA,MAAA,EAAA,KAAA,EAAA,IAAA,EAAA,SAAA,EAAA,CAAA,MAAA,CAAA,EAAA,WAAA,EAAA,IAAA,EAAA,MAAA,EAAA,IAAA,EAAA,CAAA,EAAA,aAAA,EAAA,IAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EChC7B,2gIAgGA,EAAA,MAAA,EAAA,CAAA,q8DAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EDtEY,YAAY,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,OAAA,EAAA,QAAA,EAAA,kBAAA,EAAA,MAAA,EAAA,CAAA,SAAA,EAAA,cAAA,EAAA,eAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,IAAA,EAAA,QAAA,EAAA,QAAA,EAAA,MAAA,EAAA,CAAA,MAAA,EAAA,UAAA,EAAA,UAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,gBAAA,EAAA,QAAA,EAAA,oBAAA,EAAA,MAAA,EAAA,CAAA,yBAAA,EAAA,kBAAA,EAAA,0BAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA,CAAA;;4FAMX,gBAAgB,EAAA,UAAA,EAAA,CAAA;kBAT5B,SAAS;+BACE,oBAAoB,EAAA,UAAA,EAClB,IAAI,EAAA,OAAA,EACP,CAAC,YAAY,CAAC,EAAA,eAAA,EAGN,uBAAuB,CAAC,MAAM,EAAA,QAAA,EAAA,2gIAAA,EAAA,MAAA,EAAA,CAAA,q8DAAA,CAAA,EAAA;;sBAI9C,SAAS;AAAC,gBAAA,IAAA,EAAA,CAAA,MAAM,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE;;sBAGlC;;sBAGA;;sBAGA;;sBACA;;sBAEA;;sBAEA;;sBACA;;sBAEA;;sBACA;;sBACA;;;AEpDH;;AAEG;;ACFH;;AAEG;;;;"}
package/index.d.ts ADDED
@@ -0,0 +1,160 @@
1
+ import * as i0 from '@angular/core';
2
+ import { AfterViewInit, OnChanges, OnDestroy, ElementRef, TemplateRef, EventEmitter, NgZone, ChangeDetectorRef, SimpleChanges } from '@angular/core';
3
+ import { Observable } from 'rxjs';
4
+
5
+ type TreeMapType = 'squarified' | 'vertical' | 'horizontal';
6
+ interface TreeMapOptions {
7
+ type: TreeMapType;
8
+ textField: string;
9
+ valueField: string;
10
+ colorField: string;
11
+ /**
12
+ * Поле, по которому считается цвет при включённом colorScale.
13
+ * По умолчанию совпадает с valueField, чтобы сохранить обратную совместимость.
14
+ */
15
+ colorValueField?: string;
16
+ childrenField: string;
17
+ colors: Array<string | [string, string]>;
18
+ colorScale?: TreeMapColorScale;
19
+ /** Размер заголовка контейнеров: px (для horizontal - ширина, иначе - высота) */
20
+ titleSize: number;
21
+ /** Показывать заголовок у корня (псевдо-корень не показывает никогда) */
22
+ showTopLevelTitles: boolean;
23
+ /** Если у контейнера нет value - суммировать детей */
24
+ deriveParentValueFromChildren: boolean;
25
+ /** Округление координат (как в исходнике round to 4) */
26
+ roundDecimals: number;
27
+ /** Не углубляться, если тайл меньше этого размера */
28
+ minTileSize: number;
29
+ }
30
+ interface TreeMapColorScale {
31
+ min: string;
32
+ max: string;
33
+ center?: string;
34
+ }
35
+ interface TreeMapTileContext<T = any> {
36
+ $implicit: T;
37
+ node: TreeNode<T>;
38
+ isLeaf: boolean;
39
+ }
40
+ interface TreeMapEvent<T = any> {
41
+ node: TreeNode<T>;
42
+ dataItem: T;
43
+ path: T[];
44
+ }
45
+ interface Coord {
46
+ width: number;
47
+ height: number;
48
+ top: number;
49
+ left: number;
50
+ }
51
+ interface TreeNode<T = any> {
52
+ uid: string;
53
+ level: number;
54
+ text: string;
55
+ value: number;
56
+ colorValue?: number;
57
+ color?: string;
58
+ coord: Coord;
59
+ dataItem: T | null;
60
+ children?: TreeNode<T>[];
61
+ area?: number;
62
+ vertical?: boolean;
63
+ }
64
+ interface Layout {
65
+ compute(children: TreeNode[], coord: Coord): void;
66
+ }
67
+
68
+ declare class TreeMapComponent<T = any> implements AfterViewInit, OnChanges, OnDestroy {
69
+ private zone;
70
+ private cdr;
71
+ host: ElementRef<HTMLElement>;
72
+ data: T[] | null;
73
+ data$?: Observable<T[]>;
74
+ loader?: () => Observable<T[]> | Promise<T[]> | T[];
75
+ refreshMs: number | null;
76
+ options: Partial<TreeMapOptions>;
77
+ tileTemplate?: TemplateRef<TreeMapTileContext<T>>;
78
+ titleTemplate?: TemplateRef<TreeMapTileContext<T>>;
79
+ dataBound: EventEmitter<void>;
80
+ tileClick: EventEmitter<TreeMapEvent<T>>;
81
+ tileHover: EventEmitter<TreeMapEvent<T>>;
82
+ root: TreeNode<T> | null;
83
+ hoveredUid: string | null;
84
+ private ro;
85
+ private rebuildQueued;
86
+ private uidSeq;
87
+ private colorIdx;
88
+ private sourceSub;
89
+ private get cfg();
90
+ constructor(zone: NgZone, cdr: ChangeDetectorRef);
91
+ ngAfterViewInit(): void;
92
+ ngOnChanges(changes: SimpleChanges): void;
93
+ ngOnDestroy(): void;
94
+ isLeaf(node: TreeNode<T>): boolean;
95
+ isInverse(node: TreeNode<T>): boolean;
96
+ isTitleSide(): boolean;
97
+ trackByUid: (_: number, n: TreeNode<T>) => string;
98
+ titleSizeFor(node: TreeNode<T>): number;
99
+ onTileMouseEnter(node: TreeNode<T>): void;
100
+ onTileMouseLeave(node: TreeNode<T>): void;
101
+ onTileClick(node: TreeNode<T>): void;
102
+ /** Можно дергать из родителя: this.treemap.refreshNow() */
103
+ refreshNow(): void;
104
+ private resetDataSource;
105
+ private queueRebuild;
106
+ private rebuild;
107
+ private buildNodes;
108
+ private sortTree;
109
+ private deriveValues;
110
+ private applyColors;
111
+ private applyProjectedColors;
112
+ private applyPaletteColors;
113
+ private applyFallbackColors;
114
+ private collectNodes;
115
+ private collectValues;
116
+ private layoutNode;
117
+ private roundCoordsDeep;
118
+ private buildPath;
119
+ private nextUid;
120
+ static ɵfac: i0.ɵɵFactoryDeclaration<TreeMapComponent<any>, never>;
121
+ static ɵcmp: i0.ɵɵComponentDeclaration<TreeMapComponent<any>, "stockchart-treemap", never, { "data": { "alias": "data"; "required": false; }; "data$": { "alias": "data$"; "required": false; }; "loader": { "alias": "loader"; "required": false; }; "refreshMs": { "alias": "refreshMs"; "required": false; }; "options": { "alias": "options"; "required": false; }; "tileTemplate": { "alias": "tileTemplate"; "required": false; }; "titleTemplate": { "alias": "titleTemplate"; "required": false; }; }, { "dataBound": "dataBound"; "tileClick": "tileClick"; "tileHover": "tileHover"; }, never, never, true, never>;
122
+ }
123
+
124
+ declare function getField(path: string, row: any): any;
125
+ declare function defined(value: any): boolean;
126
+ declare function toNumber(v: any): number;
127
+ declare function totalAreaOf(items: TreeNode[]): number;
128
+ declare function round(value: number): number;
129
+ declare function roundN(value: number, decimals: number): number;
130
+ declare function colorsByLength(min: string, max: string, length: number): string[];
131
+ declare function projectColorByValue(value: number, min: number, max: number, scale: TreeMapColorScale): string;
132
+ declare function colorBrightness(color?: string): number;
133
+
134
+ declare class SquarifiedLayout implements Layout {
135
+ private orientation;
136
+ compute(children: TreeNode[], coord: Coord): void;
137
+ private layoutChildren;
138
+ private squarify;
139
+ private computeDim;
140
+ private layoutLast;
141
+ private layoutRow;
142
+ private layoutVertical;
143
+ private layoutHorizontal;
144
+ private layoutChange;
145
+ private worstAspectRatio;
146
+ private layoutV;
147
+ private layoutH;
148
+ }
149
+ declare class SliceAndDiceLayout implements Layout {
150
+ private vertical;
151
+ private quotient;
152
+ constructor(vertical: boolean);
153
+ compute(children: TreeNode[], coord: Coord): void;
154
+ private sliceAndDice;
155
+ private layoutHorizontal;
156
+ private layoutVertical;
157
+ }
158
+
159
+ export { SliceAndDiceLayout, SquarifiedLayout, TreeMapComponent, colorBrightness, colorsByLength, defined, getField, projectColorByValue, round, roundN, toNumber, totalAreaOf };
160
+ export type { Coord, Layout, TreeMapColorScale, TreeMapEvent, TreeMapOptions, TreeMapTileContext, TreeMapType, TreeNode };
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "stockchart-treemap",
3
+ "version": "1.0.0",
4
+ "description": "Standalone Angular treemap component with StockChart-themed demos and mock data generators.",
5
+ "keywords": [
6
+ "angular",
7
+ "treemap",
8
+ "stockchart",
9
+ "visualization"
10
+ ],
11
+ "license": "MIT",
12
+ "publishConfig": {
13
+ "access": "public"
14
+ },
15
+ "peerDependencies": {
16
+ "@angular/common": "^20.3.0",
17
+ "@angular/core": "^20.3.0"
18
+ },
19
+ "dependencies": {
20
+ "tslib": "^2.3.0"
21
+ },
22
+ "sideEffects": false,
23
+ "module": "fesm2022/stockchart-treemap.mjs",
24
+ "typings": "index.d.ts",
25
+ "exports": {
26
+ "./package.json": {
27
+ "default": "./package.json"
28
+ },
29
+ ".": {
30
+ "types": "./index.d.ts",
31
+ "default": "./fesm2022/stockchart-treemap.mjs"
32
+ }
33
+ }
34
+ }