GrChartHeatmap
Берут, когда матрица удержания по когортам.
Обзор
Когда брать
- матрица удержания по когортам — строки когорты, колонки смещения: сразу видно, на каком месяце кривая выполаживается;
- два категориальных измерения и одна величина — часы × дни недели, сервис × версия, регион × канал;
- важна форма, а не точные числа — пятно и градиент читаются быстрее двадцати ячеек с цифрами;
- матрица разрежена —
nullоставляет ячейку пустой, и «ещё не наступило» видно как отсутствие, а не как ноль; - отклонение от нормы в обе стороны — расходящаяся шкала красит недобор и перебор разными ролями вокруг середины.
Когда взять другое
| Нужно | Берите |
|---|---|
| Сравнить величины одного измерения | GrChartBar |
| Показать ход величины во времени | GrChartLine |
| Показать точные числа, которые будут читать и сортировать | GrDataTable |
| Показать профиль по нескольким осям | GrChartRadar |
Примеры 4 у каждого — живое демо и его код
Удержание по месячным когортам
Строки — когорты, колонки — месяцы с регистрации: глаз находит месяц, где кривая выполаживается, не сравнивая тридцать чисел. Ещё не наступившие месяцы —
null: ячейка остаётся пустой, а не красится как нулевое удержание.stepsквантует цвет, чтобы легенда читалась, слотtooltipдобавляет размер когорты, аcell-clickоткрывает когорту.Retention by monthly cohortShare still paying N months after signing up · click a cohort 40100Данные графика Строка M0 M1 M2 M3 M4 M5 Apr 100 71 62 58 55 54 May 100 74 66 61 59 — Jun 100 69 60 57 — — Jul 100 77 70 — — — Aug 100 79 — — — — Sep 100 — — — — — Click a cell to open its cohort.
Выше и ниже плана
scale="diverging"сmidpointв нуле даёт недобору и перевыполнению разные роли, а «по плану» ставит в нейтральную середину. Последовательная шкала покрасила бы худший недобор тем же бледным тоном, что и малое перевыполнение.domainдержит шкалу симметричной, а не начавшийся квартал —null.Revenue against planDeviation by region and quarter -1515Данные графика Строка Q1 Q2 Q3 Q4 North 4 9 10 — South -6 -3 -8 — East 2 5 5 — West -12 -9 -12 — Central 7 3 9 — Использование функций по тарифам — словами
Четыре
stepsпревращают доли в корзины, а слотlegendназывает их — «редко», «иногда», «часто», «почти все» — с образцами изcolorAt, вместо голых порогов.high-colorпринимает любую роль темы.Feature adoption by planShare of accounts that used it in September Rare · 0–25% Some · 25–50% Many · 50–75% Most · 75–100%Данные графика Строка Starter Team Business Recurring invoices 38 64 81 Payment links 52 71 88 Multi-currency 6 22 57 API access 2 15 63 Audit log 0 9 41 Когда клиенты пишут в поддержку
Дни недели по часам, без чисел в ячейках — смысл в форме: выходные — холодная полоса, обед понедельника — самое горячее место.
steps="0"оставляет шкалу непрерывной для плавной суточной кривой, аv-model:active-cellвыводит ячейку под курсором или клавиатурой в строку над графиком.When customers write to supportPeak: Monday around 13:00553Данные графика Строка 8:00 9:00 10:00 11:00 12:00 13:00 14:00 15:00 16:00 17:00 18:00 19:00 Mon 30 37 43 48 51 53 53 51 48 43 37 30 Tue 21 28 34 39 42 44 44 42 39 34 28 21 Wed 21 28 34 39 42 44 44 42 39 34 28 21 Thu 21 28 34 39 42 44 44 42 39 34 28 21 Fri 21 28 34 39 42 44 44 42 39 34 28 21 Sat 5 7 8 10 11 11 11 11 10 8 7 5 Sun 5 7 8 10 11 11 11 11 10 8 7 5
API порождается из пакета · разойтись не может
Props
| Prop | Type | по умолчанию | Описание |
|---|---|---|---|
height | number | undefined | undefined | Высота холста в пикселях; по умолчанию 256. Данные, загрузка и пустое состояние занимают её одинаково. |
width | number | undefined | 640 | Объявленная ширина: от неё идёт первый рендер, дальше ширина замеряется. |
valueFormat | GrChartNumberFormat | undefined | undefined | Формат значений в тултипе, скрытой таблице и объявлениях: знаки после запятой и разделители. |
showLegend | boolean | undefined | undefined | Полоса шкалы с границами домена; по умолчанию `true` — без неё цвет нечем прочитать. |
tooltip | boolean | undefined | undefined | Панель со значениями под курсором; по умолчанию `true`. `false` прячет только панель: курсор, активные марки, `update:activeIndex` и `pointHover` работают, пока график интерактивен. |
loading | boolean | undefined | false | Загрузка: на месте области построения — скелет той же высоты, место под оси уже зарезервировано. |
empty | boolean | undefined | undefined | Пустое состояние явно. Не задано — график пуст, когда рисовать нечего. |
emptyText | string | undefined | undefined | Текст заглушки пустого графика. Не задан — «Нет данных» из переводов пакета. |
dataTable | "hidden" | "visible" | "off" | undefined | undefined | Таблица данных: `hidden` (по умолчанию) — только для скринридера, `visible` — под графиком, `off` — без неё. |
interactive | boolean | undefined | true | `false` — график становится картинкой: без фокуса, тултипа и клавиатуры. |
size | "md" | "xs" | "sm" | "lg" | undefined | undefined | Кегль подписей строк, колонок и ячеек по контрольной шкале ядра; по умолчанию — размер из `GrConfigProvider`. |
locale | string | undefined | undefined | BCP-47 локаль чисел и дат. Не задана — язык переводов приложения, иначе `en`. |
ariaLabel | string | undefined | undefined | Доступное имя графика. Не задано — сводка, которую собирает компонент: тип графика и объём данных. |
ariaDescription | string | undefined | undefined | Описание для скринридера — вывод, ради которого график показан; дополняет сводку, а не заменяет её. |
dataTableMaxRows | number | "auto" | undefined | undefined | Потолок строк скрытой таблицы данных. `'auto'` (по умолчанию) — столько строк, сколько можно прочитать; выше потолка таблица идёт равномерной выборкой и говорит об этом пометкой, а поточечная полнота остаётся за клавиатурой. Число задаёт свой потолок, `Infinity` снимает его совсем, `dataTable: 'off'` убирает таблицу целиком — что из этого нужно, решает приложение. |
domain | readonly [number, number] | undefined | undefined | Границы шкалы. Не заданы — считаются по данным. |
scale | HeatmapScaleKind | undefined | "sequential" | Последовательная (одна роль) или расходящаяся (две роли вокруг середины). |
midpoint | number | undefined | 0 | Середина расходящейся шкалы. |
lowColor | string | undefined | HEATMAP_LOW_COLOR | Цвет нижнего края расходящейся шкалы; по умолчанию `--gr-danger`. |
highColor | string | undefined | HEATMAP_HIGH_COLOR | Цвет верхнего края шкалы; у последовательной — единственный цвет. По умолчанию `--gr-chart-1`. |
midColor | string | undefined | HEATMAP_MID_COLOR | Цвет середины расходящейся шкалы; по умолчанию `--gr-muted`. |
steps | number | undefined | undefined | Число ступеней шкалы. `0` — непрерывная. |
cellGap | number | undefined | undefined | Зазор между ячейками в пикселях. |
showValues | boolean | "auto" | undefined | undefined | Значения в ячейках. `'auto'` — только если они помещаются. |
activeCellv-model | { x: number; y: number; } | null | undefined | undefined | Курсор — `v-model:activeCell`. |
valuesобязательный | readonly (readonly (number | null)[])[] | — | Значения построчно: `values[y][x]`. `null` — ячейки нет, а не «ноль». |
xLabelsобязательный | readonly string[] | — | Подписи колонок — по одной на колонку `values`. Не помещаются в ячейку — усекаются или прореживаются. |
yLabelsобязательный | readonly string[] | — | Подписи строк — по одной на строку `values`. Колонка под них — до 40% ширины. |
Слоты
| Slot | Type | Описание |
|---|---|---|
tooltip | { active: GrChartActivePoint; cell: GrChartHeatmapCell | null; } | Своё содержимое панели тултипа вместо стандартного; данные активной позиции — в параметрах слота. |
legend | { thresholds: readonly number[]; colorAt: (fraction: number) => string; } | Своя легенда вместо стандартной. У пустого графика не рисуется. |
empty | any | Своя заглушка пустого графика вместо «Нет данных»; занимает ту же `height`. |
header | any | Заголовок над графиком, внутри его корня — выше легенды, стоящей сверху. |
События
| Event | Сигнатура | Описание |
|---|---|---|
update:activeCell | (event: "update:activeCell", value: { x: number; y: number; } | null): void | — |
cellClick | (event: "cellClick", value: GrChartHeatmapCell): void | — |
cellHover | (event: "cellHover", value: GrChartHeatmapCell | null): void | — |
Методы
| Methods | Сигнатура | Описание |
|---|---|---|
element | HTMLElement | null | Корневой элемент — для замеров и скролла в потребителе. |
Типы
Все экспортируемые типы подпути, по файлам, где они объявлены. Имена, дженерики и тела читаются из файлов деклараций пакета и разойтись не могут.
import type { GrChartHeatmapConfigurableProps, GrChartSize, GrChartHeatmapCell } from '@feugene/granularity-charts/components/GrChartHeatmap'Файлdist/types/components/GrChartHeatmap/config.d.ts
group тот же, что у остальных графиков: рама у них общая, и её чанк тоже.
Ребром графа компонентов общий чанк не становится — иначе вынесенный хелпер
превращал бы соседей в зависимости друг друга; в файлы каждого дотянувшегося
компонента он входит, и классы рамы сборка извлекает оттуда сама.
GrEmptyState и GrSkeleton объявлены зависимостями потому, что рама их
**рендерит**: без объявления они не попадут в замыкание селекции, и
приложение не подмешает их CSS.
const grChartHeatmapConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrChartHeatmap">;Файлdist/types/components/GrChartHeatmap/defaults.d.ts
Пропы GrChartHeatmap, настраиваемые глобально через
<GrConfigProvider :component-defaults="{ GrChartHeatmap: { … } }">.
Только оформление: матрица, подписи и границы шкалы задаются на месте.
interface GrChartHeatmapConfigurableProps {
size: GrChartSize;
height: number;
/** Зазор между ячейками в пикселях: он идёт в геометрию сетки, а не в CSS. */
cellGap: number;
/** Число ступеней шкалы; `0` — непрерывная. */
steps: number;
showLegend: boolean;
showValues: boolean | 'auto';
tooltip: boolean;
dataTable: 'hidden' | 'visible' | 'off';
/**
* Потолок строк скрытой таблицы данных.
*
* Политика приложения: держать ли весь ряд в дереве доступности, усечь ли его
* до читаемого или убрать таблицу вовсе — решение уровня продукта, а не
* отдельного графика.
*/
dataTableMaxRows: number | 'auto';
}Файлdist/types/components/GrChartFrame/chartFrameStyles.d.ts
Классы общей рамы графика.
Рама — не компонент: у директории нет ни index.ts, ни config.ts, поэтому
в реестры она не попадает (гейт frameOwnership.test.ts). Владельцем токенов
--gr-chart-frame-* она при этом остаётся: гейт покомпонентных токенов ищет
реестры по файлу tokens.json в любой директории Gr…, а не по составу
компонента.
Пиксельных литералов и утилит uno-шкалы (text-sm, rounded-md) здесь нет:
темой они не настраиваются, а выглядят правильно.
type GrChartSize = GrComponentSize;Файлdist/types/components/GrChartHeatmap/safelist.d.ts
const grChartHeatmapSafelist: string[];Файлdist/types/components/GrChartHeatmap/GrChartHeatmap.vue.d.ts
Теплокарта: матрица значений, где цвет кодирует величину.
Шкала — одна роль темы через color-mix, а не набор из пяти цветов: пять
цветов пришлось бы подбирать заново под тёмную тему и заново же под вторую
теплокарту на соседней странице.
Обе оси здесь категориальные, поэтому осей рамы нет вовсе — подписи строк и
колонок компонент рисует сам, как круг рисует свои выноски. Внутрь рамы уходит
линейный ряд по индексам колонок: у категориальной оси подписи проходят через
дедупликацию, и две одинаковые колонки («Q1» за два года) слиплись бы в одну
позицию, после чего курсор адресовал бы чужую ячейку.
interface GrChartHeatmapCell {
x: number;
y: number;
xLabel: string;
yLabel: string;
value: number | null;
}interface GrChartHeatmapProps {
/** Значения построчно: `values[y][x]`. `null` — ячейки нет, а не «ноль». */
values: readonly (readonly (number | null)[])[];
/**
* Подписи колонок — по одной на колонку `values`. Не помещаются в ячейку — усекаются или
* прореживаются.
*/
xLabels: readonly string[];
/** Подписи строк — по одной на строку `values`. Колонка под них — до 40% ширины. */
yLabels: readonly string[];
/** Границы шкалы. Не заданы — считаются по данным. */
domain?: readonly [number, number];
/** Последовательная (одна роль) или расходящаяся (две роли вокруг середины). */
scale?: HeatmapScaleKind;
/** Середина расходящейся шкалы. */
midpoint?: number;
/** Цвет нижнего края расходящейся шкалы; по умолчанию `--gr-danger`. */
lowColor?: string;
/**
* Цвет верхнего края шкалы; у последовательной — единственный цвет. По умолчанию `--gr-chart-1`.
*/
highColor?: string;
/** Цвет середины расходящейся шкалы; по умолчанию `--gr-muted`. */
midColor?: string;
/** Число ступеней шкалы. `0` — непрерывная. */
steps?: number;
/** Зазор между ячейками в пикселях. */
cellGap?: number;
/** Полоса шкалы с границами домена; по умолчанию `true` — без неё цвет нечем прочитать. */
showLegend?: boolean;
/** Значения в ячейках. `'auto'` — только если они помещаются. */
showValues?: boolean | 'auto';
/**
* Формат значений в тултипе, скрытой таблице и объявлениях: знаки после запятой и разделители.
*/
valueFormat?: GrChartNumberFormat;
/**
* Высота холста в пикселях; по умолчанию 256. Данные, загрузка и пустое состояние занимают её
* одинаково.
*/
height?: number;
/** Объявленная ширина: от неё идёт первый рендер, дальше ширина замеряется. */
width?: number;
/**
* Панель со значениями под курсором; по умолчанию `true`. `false` прячет только
* панель: курсор, активные марки, `update:activeIndex` и `pointHover` работают,
* пока график интерактивен.
*/
tooltip?: boolean;
/** Курсор — `v-model:activeCell`. */
activeCell?: {
x: number;
y: number;
} | null;
/**
* Загрузка: на месте области построения — скелет той же высоты, место под оси уже
* зарезервировано.
*/
loading?: boolean;
/** Пустое состояние явно. Не задано — график пуст, когда рисовать нечего. */
empty?: boolean;
/** Текст заглушки пустого графика. Не задан — «Нет данных» из переводов пакета. */
emptyText?: string;
/**
* Таблица данных: `hidden` (по умолчанию) — только для скринридера, `visible` — под графиком,
* `off` — без неё.
*/
dataTable?: 'hidden' | 'visible' | 'off';
/** `false` — график становится картинкой: без фокуса, тултипа и клавиатуры. */
interactive?: boolean;
/**
* Кегль подписей строк, колонок и ячеек по контрольной шкале ядра; по умолчанию — размер из
* `GrConfigProvider`.
*/
size?: GrChartSize;
/** BCP-47 локаль чисел и дат. Не задана — язык переводов приложения, иначе `en`. */
locale?: string;
/**
* Доступное имя графика. Не задано — сводка, которую собирает компонент: тип графика и объём
* данных.
*/
ariaLabel?: string;
/**
* Описание для скринридера — вывод, ради которого график показан; дополняет сводку, а не заменяет
* её.
*/
ariaDescription?: string;
/**
* Потолок строк скрытой таблицы данных.
*
* `'auto'` (по умолчанию) — столько строк, сколько можно прочитать; выше
* потолка таблица идёт равномерной выборкой и говорит об этом пометкой, а
* поточечная полнота остаётся за клавиатурой. Число задаёт свой потолок,
* `Infinity` снимает его совсем, `dataTable: 'off'` убирает таблицу целиком —
* что из этого нужно, решает приложение.
*/
dataTableMaxRows?: number | 'auto';
}interface GrChartHeatmapEmits {
/** Курсор сдвинулся: ячейка `{ x, y }` или `null` — для `v-model:activeCell`. */
(e: 'update:activeCell', value: {
x: number;
y: number;
} | null): void;
/** Щелчок или Enter по ячейке — с её значением и подписями строки и колонки. */
(e: 'cellClick', value: GrChartHeatmapCell): void;
/** Курсор пришёл на ячейку или ушёл с графика (`null`). */
(e: 'cellHover', value: GrChartHeatmapCell | null): void;
}Использование из прозы библиотеки
null — не ноль и не минимум шкалы
Отсутствующая ячейка не заливается вовсе, в скрытой таблице получает прочерк и в домен шкалы не входит. Для когорт это принципиально: «месяц ещё не наступил» и «удержание ноль процентов» — разные утверждения, и покрасить первое как второе значило бы нарисовать полное вымывание там, где данных просто нет.
Строки разной длины дополняются null справа, а не нулями: матрица когорт
разрежена по построению.
Шкала — одна роль темы, а не пять цветов
Цвет ячейки считается color-mix по роли (highColor, а у расходящейся шкалы
ещё lowColor и midColor). Пять подобранных цветов пришлось бы подбирать
заново под тёмную тему и заново же — под вторую теплокарту на соседней странице;
роль подстраивается сама.
steps квантует долю: 5 (по умолчанию) даёт пять ступеней, 0 — непрерывный
градиент. Края шкалы в обоих режимах совпадают, различаются середины.
Расходящаяся шкала нормируется на больший из отступов от середины — так она симметрична по построению, а не по совпадению данных. У минимума при этом остаётся заметная примесь краски: без неё он был бы неотличим от пустой ячейки.
Легенда расшифровывает шкалу, а не перечисляет категории
Полоса с подписями границ домена. Без неё цвет нечем прочитать: у матрицы нет оси значений, и «насыщеннее» само по себе не значит ничего.
Клавиатура двумерная
←→ меняют колонку, ↑↓ — строку, Home/End уводят в край строки,
PageUp/PageDown — в край колонки. Одна остановка Tab на всю карту.
Ни одна ось не кольцуется. Перескок с конца строки на начало следующей дезориентирует: читатель теряет, в какой он строке, а сказать ему об этом нечем — объявляется только содержимое ячейки.
Скрытая таблица здесь ценнее всего
У неё настоящие заголовки строк и колонок, и это не формальность: визуальная теплокарта без таблицы нечитаема вовсе, а не просто менее удобна. Цвет незрячему не сообщает ничего, а значений на рисунке может не быть — подписи в ячейках появляются только там, где помещаются.
Подписи строк и колонок не налезают
Колонка имён строк растёт по самому длинному имени — до 40% ширины графика, но
не уже 96px; длиннее — многоточие и полное имя в <title>. Подписи колонок
стоят по центру ячеек, пока помещаются. В узкой ячейке подпись усекается, пока
от неё остаются хотя бы три знака, а дальше ряд прореживается — с первой и
последней колонкой, — и каждая оставшаяся подпись занимает место до середины
пути к соседям. Наложиться подписям не на чем, а имя любой колонки остаётся в
тултипе и скрытой таблице.
Границы
Компонент не кластеризует, не сортирует строки и колонки и не рисует дендрограмму: порядок задаёт потребитель, потому что только он знает, что здесь означает «похожие». Неравномерной сетки тоже нет — ячейки одного размера, иначе площадь начала бы кодировать второй показатель, о котором читателю не сказали.
Пропы этого примера