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 pricing 12,400 100 % — Started a trial 3,180 26 % 26 % Sent a first invoice 1,420 11 % 45 % Became paying 612 5 % 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% Данные графика Ступень Значение Доля от первой Доля от предыдущей Cart 5 240 100 % — Shipping address 4 310 82 % 82 % Payment 2 690 51 % 62 % Order confirmed 2 470 47 % 92 % Ступень, которая растёт, и нулевая ступень
Приглашения считаются по штукам, а не по аккаунтам, поэтому вторая ступень шире первой — и воронка рисует её шире, а не выпрямляет: либо ошибка в данных, либо ступени из разных когорт, и
aria-descriptionговорит, что именно. «Пока никто не перешёл» — это результат, поэтому нулевая ступень сохраняет полоску и подпись.Referral programmeInvitations outnumber the accounts that sent them Данные графика Ступень Значение Доля от первой Доля от предыдущей Accounts with referral link 940 100 % — Invitations sent 1 360 145 % 145 % Invitees signed up 410 44 % 30 % Upgraded to Enterprise 0 0 % 0 % Воронка сделок слева направо
orientation="horizontal"идёт по пути сделки через доску.labels="value"оставляет количества — на четырёх ступенях доли читаются по форме, — аstage-hoverпитает строку над графиком.Q4 pipelineHover a stageДанные графика Ступень Значение Доля от первой Доля от предыдущей Leads 420 100 % — Qualified 186 44 % 44 % Proposal sent 74 18 % 40 % Won 31 7 % 42 %
API порождается из пакета · разойтись не может
Props
| Prop | Type | по умолчанию | Описание |
|---|---|---|---|
height | number | undefined | undefined | Высота холста в пикселях; по умолчанию 256. Данные, загрузка и пустое состояние занимают её одинаково. |
width | number | undefined | 640 | Объявленная ширина: от неё идёт первый рендер, дальше ширина замеряется. |
valueFormat | GrChartNumberFormat | undefined | undefined | Формат значений в тултипе, скрытой таблице и объявлениях: знаки после запятой и разделители. |
tooltip | boolean | undefined | undefined | Панель со значениями под курсором; по умолчанию `true`. `false` прячет только панель: курсор, активные марки, `update:activeIndex` и `pointHover` работают, пока график интерактивен. |
activeIndexv-model | number | null | undefined | undefined | Курсор — `v-model:activeIndex`. |
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'` убирает таблицу целиком — что из этого нужно, решает приложение. |
orientation | "vertical" | "horizontal" | undefined | undefined | Направление: `vertical` (по умолчанию) — ступени сверху вниз, имена слева; `horizontal` — слева направо, имена под ступенями. |
labels | "none" | "value" | "share-first" | "share-prev" | undefined | undefined | Число у ступени: значение (по умолчанию), доля от первой или от предыдущей ступени. Не помещается внутри — стоит рядом. `'none'` убирает числа, имена ступеней остаются. |
shape | "trapezoid" | "bar" | undefined | undefined | Сужающаяся лента или прямоугольники одной ширины. |
gap | number | undefined | undefined | Зазор между ступенями в пикселях. |
stagesобязательный | readonly GrChartFunnelStage[] | — | Ступени по порядку: `{ label, value, color? }`. Ширина ступени пропорциональна значению, а не порядку. |
Слоты
| Slot | Type | Описание |
|---|---|---|
tooltip | { active: GrChartActivePoint; stage: GrChartFunnelActiveStage | null; } | Своё содержимое панели тултипа вместо стандартного; данные активной позиции — в параметрах слота. |
empty | any | Своя заглушка пустого графика вместо «Нет данных»; занимает ту же `height`. |
header | any | Заголовок над графиком, внутри его корня — выше легенды, стоящей сверху. |
События
| Event | Сигнатура | Описание |
|---|---|---|
update:activeIndex | (event: "update:activeIndex", value: number | null): void | — |
stageClick | (event: "stageClick", value: GrChartFunnelActiveStage): void | — |
stageHover | (event: "stageHover", value: GrChartFunnelActiveStage | null): void | — |
Методы
| Methods | Сигнатура | Описание |
|---|---|---|
element | HTMLElement | null | Корневой элемент — для замеров и скролла в потребителе. |
Типы
Все экспортируемые типы подпути, по файлам, где они объявлены. Имена, дженерики и тела читаются из файлов деклараций пакета и разойтись не могут.
import type { GrChartFunnelConfigurableProps, GrChartSize, GrChartFunnelActiveStage } from '@feugene/granularity-charts/components/GrChartFunnel'Файлdist/types/components/GrChartFunnel/config.d.ts
group тот же, что у остальных графиков: рама у них общая, и её чанк тоже.
Ребром графа компонентов общий чанк не становится — иначе вынесенный хелпер
превращал бы соседей в зависимости друг друга; в файлы каждого дотянувшегося
компонента он входит, и классы рамы сборка извлекает оттуда сама.
GrEmptyState и GrSkeleton объявлены зависимостями потому, что рама их
**рендерит**: без объявления они не попадут в замыкание селекции, и
приложение не подмешает их CSS.
const grChartFunnelConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrChartFunnel">;Файлdist/types/components/GrChartFunnel/defaults.d.ts
Пропы 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
Классы общей рамы графика.
Рама — не компонент: у директории нет ни 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
const grChartFunnelSafelist: string[];Файлdist/types/components/GrChartFunnel/GrChartFunnel.vue.d.ts
Воронка: сколько дошло до каждой ступени и где теряется больше всего. Ширина ступени пропорциональна **значению**, а не порядку: убывание рисуется потому, что оно есть в данных. Ступень больше предыдущей воронка не выпрямляет — рисует честно и говорит об этом в описании: это либо ошибка данных, либо разные когорты, и решать должен читатель. Внутрь рамы уходит линейный ряд по индексам ступеней: у категориальной оси подписи проходят через дедупликацию, и две одноимённые ступени слиплись бы в одну позицию.
interface GrChartFunnelActiveStage {
index: number;
label: string;
value: number;
/** Доля от первой ступени и от предыдущей — разные знаменатели, обе нужны. */
shareFirst: number | null;
sharePrev: number | null;
}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';
}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' рисует
прямоугольники — значения, доли и содержимое таблицы при этом совпадают до
знака. Форма здесь вопрос вкуса, а не смысла.
Клавиатура идёт по ступеням обеими парами стрелок
Воронка стоит одной колонкой, поэтому ↑↓ значат ровно то же, что ←→.
Объявляется ступень целиком: значение и обе доли.
Границы
Компонент не считает воронку из событий — значения даёт потребитель. Ветвления (нескольких путей из одной ступени) нет, сравнения двух воронок рядом — тоже.
Пропы этого примера