GrChartFunnel

  • charts
  • Графики
  • API · 20 props

Берут, когда последовательность ступеней, где каждая — подмножество предыдущей.

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

Обзор

Когда брать

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

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

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

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

  • От страницы цен до платящего клиента

    labels="share-first" считает каждую ступень от первой: «4.9% стали платящими» — то число, ради которого воронку обычно и строят. Слот tooltip получает ступень с обеими долями — от начала и от предыдущего шага, — потому что доля без знаменателя ничего не значит. stage-click открывает аккаунты, застрявшие на ступени.

    Trial funnel · SeptemberEach stage is a subset of the one before · click a stage
    Данные графика
    СтупеньЗначениеДоля от первойДоля от предыдущей
    Visited pricing12,400100 %—
    Started a trial3,18026 %26 %
    Sent a first invoice1,42011 %45 %
    Became paying6125 %43 %

    Click a stage to list its accounts.

  • Где оформление теряет больше всего

    labels="share-prev" показывает потерю на каждом шаге отдельно, и худший переход виден сразу — здесь между адресом и оплатой. shape="bar" убирает сужающуюся ленту, gap разводит шаги, а size="sm" укладывает график в карточку.

    Checkout, last 7 daysBiggest loss: Shipping address → Payment, −38%
    Данные графика
    СтупеньЗначениеДоля от первойДоля от предыдущей
    Cart5 240100 %—
    Shipping address4 31082 %82 %
    Payment2 69051 %62 %
    Order confirmed2 47047 %92 %
  • Ступень, которая растёт, и нулевая ступень

    Приглашения считаются по штукам, а не по аккаунтам, поэтому вторая ступень шире первой — и воронка рисует её шире, а не выпрямляет: либо ошибка в данных, либо ступени из разных когорт, и aria-description говорит, что именно. «Пока никто не перешёл» — это результат, поэтому нулевая ступень сохраняет полоску и подпись.

    Referral programmeInvitations outnumber the accounts that sent them
    Данные графика
    СтупеньЗначениеДоля от первойДоля от предыдущей
    Accounts with referral link940100 %—
    Invitations sent1 360145 %145 %
    Invitees signed up41044 %30 %
    Upgraded to Enterprise00 %0 %
  • Воронка сделок слева направо

    orientation="horizontal" идёт по пути сделки через доску. labels="value" оставляет количества — на четырёх ступенях доли читаются по форме, — а stage-hover питает строку над графиком.

    Q4 pipelineHover a stage
    Данные графика
    СтупеньЗначениеДоля от первойДоля от предыдущей
    Leads420100 %—
    Qualified18644 %44 %
    Proposal sent7418 %40 %
    Won317 %42 %

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

Props

PropTypeпо умолчаниюОписание
heightnumber | undefinedundefinedВысота холста в пикселях; по умолчанию 256. Данные, загрузка и пустое состояние занимают её одинаково.
widthnumber | undefined640Объявленная ширина: от неё идёт первый рендер, дальше ширина замеряется.
valueFormatGrChartNumberFormat | 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` — график становится картинкой: без фокуса, тултипа и клавиатуры.
size"md" | "xs" | "sm" | "lg" | undefinedundefinedКегль имён ступеней и подписей снаружи по контрольной шкале ядра; по умолчанию — размер из `GrConfigProvider`.
localestring | undefinedundefinedBCP-47 локаль чисел и дат. Не задана — язык переводов приложения, иначе `en`.
ariaLabelstring | undefinedundefinedДоступное имя графика. Не задано — сводка, которую собирает компонент: тип графика и объём данных.
ariaDescriptionstring | undefinedundefinedОписание для скринридера — вывод, ради которого график показан; дополняет сводку, а не заменяет её.

Слоты

SlotTypeОписание
tooltip{ active: GrChartActivePoint; stage: GrChartFunnelActiveStage | null; }Своё содержимое панели тултипа вместо стандартного; данные активной позиции — в параметрах слота.
emptyanyСвоя заглушка пустого графика вместо «Нет данных»; занимает ту же `height`.
headeranyЗаголовок над графиком, внутри его корня — выше легенды, стоящей сверху.

События

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

Методы

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

Типы

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

Импорт
import type { GrChartFunnelConfigurableProps, GrChartSize, GrChartFunnelActiveStage } from '@feugene/granularity-charts/components/GrChartFunnel'

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

grChartFunnelConfigconst

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

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

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

GrChartFunnelConfigurablePropsinterface

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

interface GrChartFunnelConfigurableProps {
    size: GrChartSize;
    height: number;
    orientation: 'vertical' | 'horizontal';
    shape: 'trapezoid' | 'bar';
    /** Что писать у ступени: долю от первой, от предыдущей или абсолют. */
    labels: 'value' | 'share-first' | 'share-prev' | 'none';
    /** Зазор между ступенями в пикселях: он идёт в геометрию, а не в CSS. */
    gap: number;
    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/GrChartFunnel/safelist.d.ts

grChartFunnelSafelistconst
const grChartFunnelSafelist: string[];

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

GrChartFunnelActiveStageinterface

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

interface GrChartFunnelActiveStage {
    index: number;
    label: string;
    value: number;
    /** Доля от первой ступени и от предыдущей — разные знаменатели, обе нужны. */
    shareFirst: number | null;
    sharePrev: number | null;
}
GrChartFunnelPropsinterface
interface GrChartFunnelProps {
    /**
     * Ступени по порядку: `{ label, value, color? }`. Ширина ступени пропорциональна значению, а не
     * порядку.
     */
    stages: readonly GrChartFunnelStage[];
    /**
     * Число у ступени: значение (по умолчанию), доля от первой или от предыдущей
     * ступени. Не помещается внутри — стоит рядом. `'none'` убирает числа, имена
     * ступеней остаются.
     */
    labels?: 'value' | 'share-first' | 'share-prev' | 'none';
    /**
     * Направление: `vertical` (по умолчанию) — ступени сверху вниз, имена слева; `horizontal` — слева
     * направо, имена под ступенями.
     */
    orientation?: 'vertical' | 'horizontal';
    /** Сужающаяся лента или прямоугольники одной ширины. */
    shape?: 'trapezoid' | 'bar';
    /** Зазор между ступенями в пикселях. */
    gap?: number;
    /**
     * Высота холста в пикселях; по умолчанию 256. Данные, загрузка и пустое состояние занимают её
     * одинаково.
     */
    height?: number;
    /** Объявленная ширина: от неё идёт первый рендер, дальше ширина замеряется. */
    width?: number;
    /**
     * Формат значений в тултипе, скрытой таблице и объявлениях: знаки после запятой и разделители.
     */
    valueFormat?: GrChartNumberFormat;
    /**
     * Панель со значениями под курсором; по умолчанию `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';
}
GrChartFunnelEmitsinterface
interface GrChartFunnelEmits {
    /** Курсор сдвинулся: индекс ступени или `null` — для `v-model:activeIndex`. */
    (e: 'update:activeIndex', value: number | null): void;
    /** Щелчок или Enter по ступени — с её значением и обеими долями. */
    (e: 'stageClick', value: GrChartFunnelActiveStage): void;
    /** Курсор пришёл на ступень или ушёл с графика (`null`). */
    (e: 'stageHover', value: GrChartFunnelActiveStage | null): void;
}

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

Две доли — два знаменателя

От первой ступени и от предыдущей. Это разные числа, и обе доступны сразу — в тултипе, в скрытой таблице и в объявлении. Смешивать их в одной подписи нельзя: «конверсия сорок процентов» без указания знаменателя не значит ничего, и именно на этой подмене строится большинство приукрашенных воронок.

Проп labels выбирает, что писать у самой ступени; остальное остаётся доступным рядом.

Имя и число у каждой ступени

Имена ступеней рисуются всегда: без них не понять, какая полоса что значит, — тултип и скрытая таблица этого не заменяют. У вертикали имена стоят колонкой слева (до 40% ширины графика, не уже 96px), у горизонтали — строкой под ступенями; длиннее — многоточие и полное имя в <title>.

Подпись значения стоит внутри ступени, если помещается, а иначе — рядом: справа от середины ступени у вертикали, над ней у горизонтали. Узкая ступень — это и есть место, где воронка теряет больше всего, поэтому подпись там не пропадает никогда. labels: 'none' убирает только числа, имена остаются.

Ширина пропорциональна значению, а не порядку

Убывание рисуется потому, что оно есть в данных. Ступень, которая больше предыдущей, не выпрямляется: она рисуется шире и попадает в ariaDescription отдельной фразой. Молча «починить» такой график значило бы скрыть либо ошибку данных, либо факт того, что ступени считаны по разным когортам.

Нулевая ступень видима

«Сюда не дошёл никто» — это результат, а не отсутствие ступени. У ступени остаётся минимальная ширина и подпись «0» рядом с ней, иначе воронка выглядела бы короче, чем есть.

Лента и полосы дают одни и те же числа

shape: 'trapezoid' сужается к следующей ступени, shape: 'bar' рисует прямоугольники — значения, доли и содержимое таблицы при этом совпадают до знака. Форма здесь вопрос вкуса, а не смысла.

Клавиатура идёт по ступеням обеими парами стрелок

Воронка стоит одной колонкой, поэтому ↑↓ значат ровно то же, что ←→. Объявляется ступень целиком: значение и обе доли.

Границы

Компонент не считает воронку из событий — значения даёт потребитель. Ветвления (нескольких путей из одной ступени) нет, сравнения двух воронок рядом — тоже.