GrChartWaterfall

  • charts
  • Графики
  • API · 25 props
  • ⌨ клавиши

Берут, когда движение от остатка к остатку.

import { GrChartWaterfall } from '@feugene/granularity-charts/components/GrChartWaterfall'

Обзор

Когда брать

  • движение от остатка к остатку — подписки, обязательства, склад: видно не только каждое изменение, но и то, как из начала месяца получился конец;
  • проверить, сходится ли баланс — шаги kind: 'total' ставят реальные значения на начало и на конец, и расхождение с суммой движений становится видно глазом, а не в уме;
  • разложить дельту между двумя числами — «выручка выросла на 12 %» и четыре причины этого роста читаются как одна история;
  • прибавления и убавления вперемешку — цвет идёт по знаку, и направление каждого шага видно до чтения подписей;
  • шагов немного — до полутора-двух десятков; дальше подписи оси налезают друг на друга, и мост стоит свернуть до крупных статей.

Когда взять другое

НужноБерите
Сравнить величины по категориям, без преемственностиGrChartBar
Показать состав одного целого на один моментGrChartPie
Показать ход величины во времениGrChartLine
Показать, где теряются пользователи между ступенямиGrChartFunnel
Показать одно число с динамикойGrStatistic

Примеры 4 у каждого — живое демо и его код

  • Из чего сложился MRR сентября

    MRR на начало — total, он рисуется от нуля; новые клиенты, расширения, сокращения и отток накапливаются на нём, вверх зелёным, вниз красным. show-total закрывает мост именованным столбцом. Слот tooltip получает каждый шаг с накоплением до и после, а step-click показывает аккаунты за движением.

    MRR bridge · SeptemberFrom €184.0k to €205.6k · click a step to see its accounts
    Данные графика
    ШагИзменениеНакоплено доНакоплено после
    Start of Sep184,00,0184,0
    New22,4184,0206,4
    Expansion9,1206,4215,5
    Reactivation1,1215,5216,6
    Contraction-3,2216,6213,4
    Churn-7,8213,4205,6
    End of Sep205,6205,6205,6
    Click a movement to list the accounts behind it.
  • Сверка склада, которая не сходится

    Оба конца — настоящие числа: остаток на начало и пересчёт, поэтому оба — шаги total, и если движения не сходятся, последний столбец обрывается раньше, чем они кончаются: 32 лампы пропали, и рисунок это говорит. Нулевой шаг рисуется чертой, а orientation="horizontal" даёт каждой подписи свою строку.

    Desk lamp, black · stock check32 units are not accounted for
    Данные графика
    ШагИзменениеНакоплено доНакоплено после
    Opening stock1 20001 200
    Received4801 2001 680
    Shipped-9101 680770
    Returns36770806
    Written off-14806792
    Transfers0792792
    Physical count760792760
  • Откуда взялся рост

    «+€142k» и четыре его причины читаются одной историей. Рядом с €1,2m второго квартала валютный эффект в €19k был бы волоском, а ось, обрезанная выше нуля, соврала бы о величинах, — поэтому мост объясняет само изменение, от нуля, а show-total называет, куда оно пришло.

    Revenue Q2 → Q3: +€142kVolume did most of it; mix and the dollar took some back
    Данные графика
    ШагИзменениеНакоплено доНакоплено после
    Price64064
    Volume11864182
    Mix-21182161
    FX-19161142
    Change142142142
  • Отчёт о прибылях с промежуточными итогами

    Валовая и операционная прибыль — строки отчёта, а не дельты: kind: "total" рисует их от нуля и начинает накопление заново, так что читателю не приходится складывать столбцы ради числа, у которого уже есть имя. show-connectors соединяет движения и обрывается на каждом итоге.

    Income statement · Q3 2026Thousands of eurosNet margin 15.7%
    Данные графика
    ШагИзменениеНакоплено доНакоплено после
    Revenue1 24001 240
    Cost of sales-4101 240830
    Gross profit830830830
    Salaries-390830440
    Ads-120440320
    Rent-60320260
    Operating profit260260260
    Tax-65260195
    Net profit195195195

API порождается из пакета · разойтись не может

Props

PropTypeпо умолчаниюОписание
heightnumber | undefinedundefinedВысота холста в пикселях; по умолчанию 256. Данные, загрузка и пустое состояние занимают её одинаково.
widthnumber | undefined640Объявленная ширина: от неё идёт первый рендер, дальше ширина замеряется.
yDomainreadonly [number | null, number | null] | undefinedundefinedГраницы оси значений `[min, max]`; `null` на месте границы — она берётся по данным.
yTickCountnumber | undefined5Желаемое число делений оси значений; по умолчанию 5. Шаг остаётся «круглым», поэтому делений бывает на одно меньше.
yTickFormat((value: number) => string) | undefinedundefinedПодпись деления оси значений, например `value => value + '%'`. Место под ось считается по отформатированной подписи.
valueFormatGrChartNumberFormat | undefinedundefinedФормат значений в тултипе, скрытой таблице и объявлениях: знаки после запятой и разделители.
showGrid"y" | "both" | "x" | "none" | undefinedundefinedКакая сетка нужна. Оси названы по данным: при горизонтали они меняются местами сами.
tooltipboolean | undefinedundefinedПанель со значениями под курсором; по умолчанию `true`. `false` прячет только панель: курсор, активные марки, `update:activeIndex` и `pointHover` работают, пока график интерактивен.
activeIndexv-modelnumber | null | undefinedundefinedКурсор — `v-model:activeIndex`.
loadingboolean | undefinedfalseЗагрузка: на месте области построения — скелет той же высоты, место под оси уже зарезервировано.
emptyboolean | undefinedundefinedПустое состояние явно. Не задано — график пуст, когда рисовать нечего.
emptyTextstring | undefinedundefinedТекст заглушки пустого графика. Не задан — «Нет данных» из переводов пакета.
dataTable"hidden" | "visible" | "off" | undefinedundefinedТаблица данных: `hidden` (по умолчанию) — только для скринридера, `visible` — под графиком, `off` — без неё.
interactiveboolean | undefinedtrue`false` — график становится картинкой: без фокуса, тултипа и клавиатуры.

Слоты

SlotTypeОписание
tooltip
{ active: GrChartActivePoint; step: GrChartWaterfallActiveSt{ active: GrChartActivePoint; step: GrChartWaterfallActiveStep | null; formatValue: (value: number | null) => string; }
Своё содержимое панели тултипа вместо стандартного; данные активной позиции — в параметрах слота.
emptyanyСвоя заглушка пустого графика вместо «Нет данных»; занимает ту же `height`.
headeranyЗаголовок над графиком, внутри его корня — выше легенды, стоящей сверху.

События

EventСигнатураОписание
update:activeIndex(event: "update:activeIndex", value: number | null): void—
stepClick(event: "stepClick", value: GrChartWaterfallActiveStep): void—
stepHover(event: "stepHover", value: GrChartWaterfallActiveStep | null): void—

Методы

MethodsСигнатураОписание
elementHTMLElement | nullКорневой элемент — для замеров и скролла в потребителе.

Типы

Все экспортируемые типы подпути, по файлам, где они объявлены. Имена, дженерики и тела читаются из файлов деклараций пакета и разойтись не могут.

Импорт
import type { GrChartWaterfallConfigurableProps, GrChartSize, GrChartWaterfallActiveStep } from '@feugene/granularity-charts/components/GrChartWaterfall'

Файлdist/types/components/GrChartWaterfall/config.d.ts

grChartWaterfallConfigconst

group тот же, что у остальных графиков: рама у них общая, и её чанк тоже. Ребром графа компонентов общий чанк не становится — иначе вынесенный хелпер превращал бы соседей в зависимости друг друга; в файлы каждого дотянувшегося компонента он входит, и классы рамы сборка извлекает оттуда сама. GrEmptyState и GrSkeleton объявлены зависимостями потому, что рама их **рендерит**: без объявления они не попадут в замыкание селекции, и приложение не подмешает их CSS.

const grChartWaterfallConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrChartWaterfall">;

Файлdist/types/components/GrChartWaterfall/defaults.d.ts

GrChartWaterfallConfigurablePropsinterface

Пропы GrChartWaterfall, настраиваемые глобально через <GrConfigProvider :component-defaults="{ GrChartWaterfall: { … } }">. Только оформление: шаги, начальное накопление и границы оси задаются на месте.

interface GrChartWaterfallConfigurableProps {
    size: GrChartSize;
    height: number;
    /** Скругление дальнего конца столбца в пикселях: радиус идёт в геометрию пути, а не в CSS. */
    barRadius: number;
    /** Линии от вершины предыдущего столбца к основанию следующего. */
    showConnectors: boolean;
    orientation: 'vertical' | 'horizontal';
    showGrid: 'both' | 'x' | 'y' | 'none';
    tooltip: boolean;
    dataTable: 'hidden' | 'visible' | 'off';
    /**
     * Потолок строк скрытой таблицы данных.
     *
     * Политика приложения: держать ли весь ряд в дереве доступности, усечь ли его
     * до читаемого или убрать таблицу вовсе — решение уровня продукта, а не
     * отдельного графика.
     */
    dataTableMaxRows: number | 'auto';
}

Файлdist/types/components/GrChartFrame/chartFrameStyles.d.ts

GrChartSizetype

Классы общей рамы графика. Рама — не компонент: у директории нет ни index.ts, ни config.ts, поэтому в реестры она не попадает (гейт frameOwnership.test.ts). Владельцем токенов --gr-chart-frame-* она при этом остаётся: гейт покомпонентных токенов ищет реестры по файлу tokens.json в любой директории Gr…, а не по составу компонента. Пиксельных литералов и утилит uno-шкалы (text-sm, rounded-md) здесь нет: темой они не настраиваются, а выглядят правильно.

type GrChartSize = GrComponentSize;

Файлdist/types/components/GrChartWaterfall/safelist.d.ts

grChartWaterfallSafelistconst
const grChartWaterfallSafelist: string[];

Файлdist/types/components/GrChartWaterfall/GrChartWaterfall.vue.d.ts

GrChartWaterfallActiveStepinterface

Мост: как из начала периода получился конец. Расходящиеся столбцы отвечают «сколько пришло и сколько ушло»; мост отвечает «как одно превратилось в другое» — каждый столбец начинается там, где кончился предыдущий. Вся арифметика в chart/chartWaterfall.ts, здесь только рисунок. Ось шагов категориальная, но категорией становится **индекс**, а не подпись: две «Корректировки» подряд — обычное дело для моста, а нормализация схлопывает одинаковые категории внутри серии, и курсор после этого адресовал бы чужой шаг. Настоящие подписи приезжают на ось через xTickFormat.

interface GrChartWaterfallActiveStep {
    index: number;
    label: string;
    value: number;
    /** Накопление до и после шага. */
    before: number;
    after: number;
    kind: 'delta' | 'total';
}
GrChartWaterfallPropsinterface
interface GrChartWaterfallProps {
    /**
     * Шаги моста: `{ label, value, kind?, color? }`. `kind: 'total'` — абсолютное значение: столбец
     * от нуля, накопление сбрасывается на него.
     */
    steps: readonly GrChartWaterfallStep[];
    /** Начальное накопление. */
    baseline?: number;
    /** Дорисовать итоговый столбец справа. Строка задаёт его подпись. */
    showTotal?: boolean | string;
    /**
     * Соединители между столбцами — показывают, что следующий шаг начинается там, где кончился
     * прошлый; по умолчанию `true`.
     */
    showConnectors?: boolean;
    /** Скругление столбцов в пикселях. */
    barRadius?: number;
    /**
     * Направление: `vertical` (по умолчанию) или `horizontal` — длинные имена шагов читаются строкой.
     */
    orientation?: 'vertical' | 'horizontal';
    /**
     * Высота холста в пикселях; по умолчанию 256. Данные, загрузка и пустое состояние занимают её
     * одинаково.
     */
    height?: number;
    /** Объявленная ширина: от неё идёт первый рендер, дальше ширина замеряется. */
    width?: number;
    /** Границы оси значений `[min, max]`; `null` на месте границы — она берётся по данным. */
    yDomain?: readonly [number | null, number | null];
    /**
     * Желаемое число делений оси значений; по умолчанию 5. Шаг остаётся «круглым», поэтому делений
     * бывает на одно меньше.
     */
    yTickCount?: number;
    /**
     * Подпись деления оси значений, например `value => value + '%'`. Место под ось считается по
     * отформатированной подписи.
     */
    yTickFormat?: (value: number) => string;
    /**
     * Формат значений в тултипе, скрытой таблице и объявлениях: знаки после запятой и разделители.
     */
    valueFormat?: GrChartNumberFormat;
    /** Какая сетка нужна. Оси названы по данным: при горизонтали они меняются местами сами. */
    showGrid?: 'both' | 'x' | 'y' | 'none';
    /**
     * Панель со значениями под курсором; по умолчанию `true`. `false` прячет только
     * панель: курсор, активные марки, `update:activeIndex` и `pointHover` работают,
     * пока график интерактивен.
     */
    tooltip?: boolean;
    /** Курсор — `v-model:activeIndex`. */
    activeIndex?: 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';
}
export interface GrChartWaterfallEmits {
    /** Курсор сдвинулся: индекс шага или `null` — для `v-model:activeIndex`. */
    (e: 'update:activeIndex', value: number | null): void;
    /** Щелчок или Enter по шагу — с его приростом и накоплением. */
    (e: 'stepClick', value: GrChartWaterfallActiveStep): void;
    /** Курсор пришёл на шаг или ушёл с графика (`null`). */
    (e: 'stepHover', value: GrChartWaterfallActiveStep | null): void;
}
type __VLS_Slots = {
    /**
     * Своё содержимое панели тултипа вместо стандартного; данные активной позиции — в параметрах
     * слота.
     */
    tooltip?: (props: {
        active: GrChartActivePoint;
        step: GrChartWaterfallActiveStep | null;
        formatValue: (value: number | null) => string;
GrChartWaterfallEmitsinterface
interface GrChartWaterfallEmits {
    /** Курсор сдвинулся: индекс шага или `null` — для `v-model:activeIndex`. */
    (e: 'update:activeIndex', value: number | null): void;
    /** Щелчок или Enter по шагу — с его приростом и накоплением. */
    (e: 'stepClick', value: GrChartWaterfallActiveStep): void;
    /** Курсор пришёл на шаг или ушёл с графика (`null`). */
    (e: 'stepHover', value: GrChartWaterfallActiveStep | null): void;
}

Доступность

Паттерн APG

При orientation: 'horizontal' шаги идут сверху вниз, и по ним ходят ↓/↑

КлавишаДействие
—шаг

Использование из прозы библиотеки

Шаг total объявляет накопление, а не прибавляется к нему

kind: 'delta' (по умолчанию) — изменение от текущего накопления, kind: 'total' — абсолютное значение: столбец рисуется от нуля, а накопление сбрасывается на него. Это и позволяет поставить «На начало» и «На конец» реальными числами с бэкенда: если мост не сходится, последний столбец не совпадёт с вершиной предпоследнего, и ошибку в данных будет видно.

Соединитель к такому шагу не рисуется. Он не продолжает накопление, а объявляет его, и линия «отсюда сюда» соврала бы о преемственности.

Цвет идёт по знаку, а не по индексу серии

Мост — один ряд, и палитра серий здесь ничего не различает: различать надо прибавление (--gr-success) и убавление (--gr-danger). Итог и объявленное накопление красятся нейтрально (--gr-chart-1) — это не движение. Явный color шага сильнее любого из этих правил.

Нулевой шаг рисуется чертой

«Движения не было» — это факт, а не отсутствие данных. Столбец нулевой высоты пропал бы с рисунка, и читатель увидел бы историю, в которой такого шага нет вовсе. Вместо этого на уровне накопления встаёт черта толщиной --gr-chart-waterfall-zero-step.

Таблица несёт накопление, а не одну дельту

Скрытая таблица даёт три числа на шаг: изменение, накопление до и накопление после. По одной дельте мост не восстановить — высоту столбца задаёт именно накопление, и читающий без зрения обязан видеть то же, что зрячий видит по положению полосы.

Категория шага — его индекс, а не подпись

Две «Корректировки» подряд в мосте — обычное дело, а нормализация схлопывает одинаковые категории внутри серии. Поэтому внутрь рамы уходит индекс шага, а настоящие подписи приезжают на ось отдельно: иначе позиций стало бы меньше, чем шагов, и курсор с клавиатурой поехали бы по чужим данным.

Ось значений — по накоплениям

Домен оси считается по основаниям и вершинам столбцов вместе с нулём, а не по значениям шагов: дельта −410 на накоплении в 1240 не тянет ось в минус, где не стоит ни один столбец. yDomain сильнее: если его низ выше нуля, столбец, идущий от нуля, обрывается на краю области — обрыв и говорит, что ось начинается не с нуля, — а соединители и нулевые шаги вне домена не рисуются.

Каждый шаг назван

Подписи шагов не прореживаются, как у категорий столбцов: мост без имени шага не прочитать. Подпись, которой тесно, переносится по словам на две строки, а дальше кончается многоточием с полным именем в <title>; соседние подписи не налезают друг на друга. Если и после переноса от какой-то подписи остаётся меньше четырёх знаков — на узком холсте «Pr…» вместо «Price», — подписи встают в шахматку: соседние в разные ряды, и каждой достаётся ширина двух шагов. Усечённая подпись не бывает короче четырёх знаков.

При горизонтали имена стоят колонкой слева, до 40% ширины, ось значений оставляет место под последнюю подпись деления, а её подписи, которым тесно, прореживаются через одну — первая и последняя остаются.

Границы

Мост не считает, из чего состоят его шаги, — их даёт потребитель. Мелкие шаги в «Прочее» он тоже не группирует: только вызывающий знает, что считать мелким. Вложенных (двухуровневых) мостов нет.