AI Сделал бесплатный опенсорс TreeMap для Angular

AI

Редактор
Регистрация
23 Август 2023
Сообщения
3 600
Лучшие ответы
0
Реакции
0
Баллы
243
Offline
#1

Standalone TreeMap для Angular без лишних зависимостей: как я сделал stockchart-treemap


TreeMap — это визуализация, где площадь прямоугольника = вес, а цвет = метрика. Отлично подходит для market heatmap (карта рынка), портфелей, иерархий ресурсов и любых “взвешенных деревьев”.

Мне TreeMap понадобился в Angular-проекте под “тепловые карты” и разные иерархические отчёты. Казалось бы — задача стандартная, значит решение должно быть “в один npm install”. Но реальность оказалась неожиданной: готовых TreeMap-решений именно для Angular практически нет.

В итоге я сделал свой standalone компонент и оформил его в npm-пакет: stockchart-treemap.

Дисклеймер


  • Пакет бесплатный.


  • Это не реклама — мне никто не заплатил ни копейки.


  • Я делал компонент в первую очередь для своих задач, а потом упаковал так, чтобы можно было переиспользовать и не зависеть от внешних UI-комбайнов.
Почему не использовать готовое решение


Вот тут самое забавное: обычно в статьях пишут “не подошло потому что тяжелое/дорогое/не то API”. У меня было проще — мне просто не удалось найти нормальный готовый TreeMap под Angular.

Что я видел в процессе поисков:


  1. Почти всё — под чистый JS, React или Vue
    TreeMap как визуализация часто встречается в “JS-мире”: примеры на D3-подобных штуках, готовые виджеты, демки. Но это не Angular-компонент, а код, который сам рисует DOM/SVG/Canvas.

В Angular это превращается в вечную обвязку:


  • ElementRef, ngAfterViewInit, ручной lifecycle,


  • чистка при destroy,


  • ручные обновления при изменении данных,


  • танцы с change detection,


  • и “почему оно перерисовалось два раза/не там/не тогда”.

То есть формально “решение есть”, но по ощущениям это уже не “встроил компонент”, а “интегрировал чужой движок”.


  1. То, что называется “Angular TreeMap”, часто:

  • устаревшее (старые версии Angular, нет standalone),


  • или это тонкая обёртка над JS-ядром без нормального Angular UX (шаблоны, события, типизация, обновления данных — всё “на честном слове”).

  1. Есть варианты в составе больших UI-комбайнов
    Иногда это нормально, но лично мне не хотелось тащить “полплатформы” ради одного TreeMap. Особенно когда в проекте уже есть своя дизайн-система и свои компоненты.

Итог: формально решений много, но “поставил и получил нативный Angular-компонент с понятной интеграцией” — мне не попалось. Поэтому я решил сделать компонент так, как мне самому было бы удобно его использовать.

Что получилось: stockchart-treemap


stockchart-treemap — standalone TreeMap компонент для Angular:


  • без внешних runtime-зависимостей (никаких Kendo UI и т.п.),


  • с цветовой шкалой по значениям (min/center/max),


  • с кастомными шаблонами плиток и заголовков,


  • с несколькими режимами источника данных,


  • с событиями клика/ховера и “путём” до узла.
Демо


StackBlitz: https://stackblitz.com/edit/stackblitz-starters-drgync8z?file=README.md

Установка


npm install stockchart-treemap


Peer deps: @angular/core и @angular/common 20.x.

Быстрый старт


Пример “из коробки”: просто массив + настройки полей.

import { Component } from '@angular/core';
import { TreeMapComponent, TreeMapOptions } from 'stockchart-treemap';

@Component({
standalone: true,
selector: 'app-treemap-demo',
imports: [TreeMapComponent],
template: `
<stockchart-treemap
[data]="data"
[options]="options">
</stockchart-treemap>
`
})
export class TreemapDemoComponent {
data = [
{ name: 'Tech', value: 35, change: 4.2, items: [
{ name: 'A', value: 20, change: 5.1 },
{ name: 'B', value: 15, change: 2.0 }
]},
{ name: 'Energy', value: 25, change: -3.4 },
{ name: 'Health', value: 18, change: 1.6 }
];

options: Partial<TreeMapOptions> = {
textField: 'name',
valueField: 'value',
childrenField: 'items',

// цвет — по "change"
colorValueField: 'change',
colorScale: { min: '#E53935', center: '#F5F5F5', max: '#2E7D32' },

// оставляем пустым, чтобы полагаться на colorScale
colors: []
};
}

Источники данных: три режима, потому что в жизни по-разному


Визуализация — это всегда “последний метр” после данных. И данные могут приходить как угодно, поэтому я сделал три режима.

1) Обычный массив


Самый простой: [data]="data".

2) Observable


Если данные живые (RxJS-пайплайн, сокеты, периодические обновления на вашей стороне) — можно отдать data$, компонент подпишется и будет перерисовываться при новых значениях.

3) Loader-функция + polling


Иногда удобнее дать компоненту “как загрузить данные”, а он сам будет обновляться раз в N миллисекунд:

loader = () => this.http.get<Item[]>('/api/treemap');
refreshMs = 5000; // обновление каждые 5 секунд


Плюс есть refreshNow() — полезно для “обновить по кнопке” или после смены фильтров.

Цвета: палитра или шкала по значению


Мне нужны были оба сценария:

Палитра


Когда цвет — это категория (например, разные отделы/типы). Можно задать:


  • colors: ['#...', '#...', ...]


  • либо пары [min,max], чтобы генерировать градиенты по уровням.
Шкала по значению (heatmap)


Классика для “карты рынка”: у элемента есть метрика (например, change), и цвет вычисляется по шкале:


  • min — красный,


  • center — нейтральный,


  • max — зелёный.

Работает так:


  • задаём colorValueField,


  • задаём colorScale,


  • и получаем прогнозируемый градиент.

Если у данных вообще нет цветового поля — оттенки для вложенных уровней считаются автоматически, чтобы дерево было читабельным.

Layout-ы: squarified и slice-and-dice


Я сделал два варианта разметки:


  • squarified (по умолчанию) — стремится к “квадратности” плиток, обычно выглядит лучше.


  • horizontal / vertical — slice-and-dice, нарезка полосами с чередованием ориентации по уровням.

Это покрывает большую часть практических кейсов без превращения компонента в “комбайн настроек”.

Кастомные шаблоны: плитка и заголовок


TreeMap без нормальных шаблонов быстро превращается в “красивую мозаику”, из которой нельзя извлечь смысл.

Можно задать отдельно:


  • titleTemplate — заголовок контейнеров,


  • tileTemplate — содержимое плитки (обычно лист).

Пример:

<stockchart-treemap
[data]="data"
[options]="options"
[tileTemplate]="tile"
[titleTemplate]="title"
(tileClick)="onTile($event)">
</stockchart-treemap>

<ng-template #title let-item let-node="node">
<div class="title">
{{ item.name }} · {{ node.value | number:'1.0-0' }}
</div>
</ng-template>

<ng-template #tile let-item let-isLeaf="isLeaf">
<div class="tile">
<strong>{{ item.name }}</strong>
<span *ngIf="isLeaf">{{ item.change }}%</span>
</div>
</ng-template>

События и “путь” до узла


В интерактивных сценариях важно не только “что кликнули”, но и “где это в дереве”.

Поэтому в событиях есть:


  • dataItem — исходный объект,


  • path — цепочка элементов от корня до выбранной ноды.

import { TreeMapEvent } from 'stockchart-treemap';

onTile(ev: TreeMapEvent<MyNode>) {
console.log(ev.dataItem);
console.log(ev.path); // полезно для breadcrumbs / drilldown
}

Ключевые опции


То, что реально используется чаще всего:


  • textField / valueField / childrenField / colorField


  • colorValueField — поле для расчёта colorScale


  • colors — палитра (если пусто — можно полагаться на colorScale или fallback)


  • titleSize, showTopLevelTitles


  • deriveParentValueFromChildren — если у контейнера нет value, можно вывести из детей


  • roundDecimals — помогает стабилизировать координаты


  • minTileSize — минимальный размер плитки для рекурсии
Немного про “как я делал”


Я сознательно старался избежать “магии”. Внутри всё раскладывается по шагам:


  1. Нормализация данных (любой источник → массив нод).


  2. Подсчёт значений (в т.ч. вычисление контейнеров из детей — опционально).


  3. Layout (squarified / slice-and-dice) рекурсивно разбивает прямоугольник.


  4. Цвет (палитра или интерполяция по шкале).


  5. Рендер + шаблоны + события.

На практике это даёт главное: предсказуемость и возможность легко встроить компонент в проект, не подстраиваясь под чужие решения.

Где это полезно


  • Market heatmap: сектор → индустрия → компания (площадь = вес, цвет = % change).


  • Портфель: активы → позиции (площадь = доля, цвет = PnL).


  • Ресурсы: департаменты → проекты → сервисы (площадь = расходы/нагрузка, цвет = отклонение).


  • Любые метрики в дереве: storage usage, распределение задач, coverage, KPI и т.д.
Ограничения


Если попытаться отображать десятки тысяч нод одновременно — DOM начнёт страдать (как и у большинства UI-визуализаций). Этот компонент рассчитан на нормальные продуктовые сценарии, а не на экстремальные бенчмарки.

Если когда-нибудь понадобится “очень много элементов” — это уже территория Canvas/WebGL/виртуализации.

Заключение


stockchart-treemap появился из простой причины: TreeMap для Angular почему-то оказался редкостью, а интегрировать JS-виджеты “как есть” в Angular — это отдельный проект по обвязке.

Я сделал standalone компонент, который ставится из npm, нормально живёт в Angular, поддерживает разные источники данных, цветовые шкалы и кастомные шаблоны.

Демо: https://stackblitz.com/edit/stackblitz-starters-drgync8z?file=README.md
Пакет: npm install stockchart-treemap
 
Яндекс.Метрика Рейтинг@Mail.ru
Сверху Снизу