GrChartBullet
Берут, когда метрика с порогами, которые уже есть в данных.
Обзор
Когда брать
- метрика с порогами, которые уже есть в данных —
warningиcriticalприходят с бэкенда и обычно нигде не нарисованы: видно «плохо», не видно «насколько»; - план и факт рядом — засечка цели и полоса значения читаются одним взглядом, без вычитания в уме;
- несколько метрик подряд — bullet занимает строку, и десяток метрик встаёт списком, сравнимым по вертикали;
- место дорого — карточка, строка таблицы, шапка дашборда: тот же смысл, что у циферблата, при впятеро меньшей площади;
- величина может отсутствовать —
value: nullрисует цель и диапазоны без полосы, а не ноль.
Когда взять другое
| Нужно | Берите |
|---|---|
| Сравнить величины нескольких категорий между собой | GrChartBar |
| Показать ход величины во времени | GrChartLine |
| Показать долю выполнения одной задачи | GrProgressCircle |
| Показать одно число крупно, с динамикой | GrStatistic |
Примеры 4 у каждого — живое демо и его код
Цели задержки, сервис на строку
rangesкрасят дорожку полосами — хорошо, терпимо, плохо, — аtargetрисует поперёк цель: «182» само по себе мало что говорит, «182 из 200, в хорошей полосе» — всё. Bullet занимает строку, и таблица сервисов встаёт там, где стоял бы один циферблат. Сервис без трафика получаетvalue: null: засечка и полосы остаются, полосы значения нет — нет данных не значит ноль.value-clickоткрывает сервис.Latency objectives · p95, last hourThe mark is the objective; the bands are good, tolerable and bad Checkout API182 msДанные графика Показатель Значение Цель Checkout API 182 200 Диапазоны: от 0 до 200; от 200 до 300; от 300 до 400 Search96 msДанные графика Показатель Значение Цель Search 96 150 Диапазоны: от 0 до 150; от 150 до 250; от 250 до 300 Invoices341 msДанные графика Показатель Значение Цель Invoices 341 250 Диапазоны: от 0 до 250; от 250 до 350; от 350 до 450 Exportsno trafficДанные графика Показатель Значение Цель Exports нет значения 900 Диапазоны: от 0 до 900; от 900 до 1 500; от 1 500 до 2 000 Click a bar to open the service.
План продаж, где больше — лучше
Полосы по умолчанию идут от «хорошо» к «плохо» с ростом величины — верно для задержки, неверно для плана.
range-colorsкрасит их полоса за полосой: красный до 70 %, янтарный до плана, зелёный выше.colorзадаёт тон самой полосы значения, аsize="xs"укладывает четверых в маленькую карточку.Quota attainment · OctoberTarget is 100 % of the personal quota Grace Wu114%Данные графика Показатель Значение Цель Grace Wu 1,14 1,00 Диапазоны: от 0,00 до 0,70; от 0,70 до 1,00; от 1,00 до 1,30 Omar Haddad96%Данные графика Показатель Значение Цель Omar Haddad 0,96 1,00 Диапазоны: от 0,00 до 0,70; от 0,70 до 1,00; от 1,00 до 1,30 Ines Novak81%Данные графика Показатель Значение Цель Ines Novak 0,81 1,00 Диапазоны: от 0,00 до 0,70; от 0,70 до 1,00; от 1,00 до 1,30 Linus Ray58%Данные графика Показатель Значение Цель Linus Ray 0,58 1,00 Диапазоны: от 0,00 до 0,70; от 0,70 до 1,00; от 1,00 до 1,30 Бюджет, который уже превышен
Маркетинг потратил больше всего годового бюджета: полоса упирается в
maxс маркером переполнения, а не обрезается молча, и настоящая величина остаётся в тултипе, скрытой таблице и объявлении. Слотtooltipдобавляет долю бюджета, аaria-descriptionобъясняет, что значит засечка.Budgets after nine monthsThe mark is the share of the year gone — 75 % of the budget Engineering€1420k of €2000kДанные графика Показатель Значение Цель Engineering 1 420 1 500 Диапазоны: от 0 до 1 500; от 1 500 до 1 800; от 1 800 до 2 000 Marketing€980k of €800kДанные графика Показатель Значение Цель Marketing 980 600 Диапазоны: от 0 до 600; от 600 до 720; от 720 до 800 Sales€610k of €900kДанные графика Показатель Значение Цель Sales 610 675 Диапазоны: от 0 до 675; от 675 до 810; от 810 до 900 Плитки складов снизу вверх
У узкой плитки дашборда есть высота, а ширины нет:
orientation="vertical"сохраняет ту же величину, цель и полосы, только читаются они снизу вверх.labelназывает метрику для тултипа и таблицы данных, а подпись на плитке пишет страница — она знает, где плитка стоит.Warehouse capacityPallet places in use · target 80 % Hamburg81% full812 of 1000Данные графика Показатель Значение Цель Hamburg 812 800 Диапазоны: от 0 до 850; от 850 до 950; от 950 до 1 000 Bremen71% full640 of 900Данные графика Показатель Значение Цель Bremen 640 720 Диапазоны: от 0 до 765; от 765 до 855; от 855 до 900 Rotterdam98% full1170 of 1200Данные графика Показатель Значение Цель Rotterdam 1 170 960 Диапазоны: от 0 до 1 020; от 1 020 до 1 140; от 1 140 до 1 200
API порождается из пакета · разойтись не может
Props
| Prop | Type | по умолчанию | Описание |
|---|---|---|---|
height | number | undefined | undefined | Высота холста в пикселях; по умолчанию 48. Данные, загрузка и пустое состояние занимают её одинаково. |
width | number | undefined | 640 | Объявленная ширина: от неё идёт первый рендер, дальше ширина замеряется. |
valueFormat | GrChartNumberFormat | undefined | undefined | Формат значений в тултипе, скрытой таблице и объявлениях: знаки после запятой и разделители. |
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 | Доступное имя графика. Не задано — сводка из `label`, величины и цели. |
ariaDescription | string | undefined | undefined | Описание для скринридера — вывод, ради которого график показан; дополняет сводку, а не заменяет её. |
dataTableMaxRows | number | "auto" | undefined | undefined | Потолок строк скрытой таблицы данных. `'auto'` (по умолчанию) — столько строк, сколько можно прочитать; выше потолка таблица идёт равномерной выборкой и говорит об этом пометкой, а поточечная полнота остаётся за клавиатурой. Число задаёт свой потолок, `Infinity` снимает его совсем, `dataTable: 'off'` убирает таблицу целиком — что из этого нужно, решает приложение. |
orientation | "vertical" | "horizontal" | undefined | undefined | Направление дорожки: `horizontal` (по умолчанию) или `vertical`. |
target | number | undefined | undefined | Целевое значение — засечка поперёк дорожки. |
ranges | readonly number[] | undefined | undefined | Границы качественных диапазонов: `[0.9, 1.0]` при `max: 1.2` даёт три полосы — до 0.9, 0.9…1.0 и свыше 1.0. Порядок не важен. |
max | number | undefined | undefined | Верх шкалы. Не задан — максимум из величины, цели и границ с запасом. |
min | number | undefined | undefined | Низ шкалы; по умолчанию 0. Полоса bullet без нуля искажает длину — величину читают по ней. |
rangeColors | readonly string[] | undefined | undefined | Цвета полос от «хорошо» к «плохо». Длина на единицу больше `ranges`. |
color | string | undefined | undefined | Тон самой полосы значения. |
label | string | undefined | undefined | Имя метрики: заголовок строки в таблице и в тултипе. |
valueобязательный | number | null | — | Измеряемая величина. `null` — величины нет; это не ноль. |
Слоты
| Slot | Type | Описание |
|---|---|---|
tooltip | { active: GrChartActivePoint; formatValue: (value: number | null) => string; } | Своё содержимое панели тултипа вместо стандартного; данные активной позиции — в параметрах слота. |
empty | any | Своя заглушка пустого графика вместо «Нет данных»; занимает ту же `height`. |
header | any | Заголовок над графиком, внутри его корня — выше легенды, стоящей сверху. |
События
| Event | Сигнатура | Описание |
|---|---|---|
valueClick | (event: "valueClick", value: number | null): void | — |
Методы
| Methods | Сигнатура | Описание |
|---|---|---|
element | HTMLElement | null | Корневой элемент — для замеров и скролла в потребителе. |
Типы
Все экспортируемые типы подпути, по файлам, где они объявлены. Имена, дженерики и тела читаются из файлов деклараций пакета и разойтись не могут.
import type { GrChartBulletConfigurableProps, GrChartSize, GrChartBulletProps } from '@feugene/granularity-charts/components/GrChartBullet'Файлdist/types/components/GrChartBullet/config.d.ts
group тот же, что у остальных графиков: рама у них общая, и её чанк тоже.
Ребром графа компонентов общий чанк не становится — иначе вынесенный хелпер
превращал бы соседей в зависимости друг друга; в файлы каждого дотянувшегося
компонента он входит, и классы рамы сборка извлекает оттуда сама.
GrEmptyState и GrSkeleton объявлены зависимостями потому, что рама их
**рендерит**: без объявления они не попадут в замыкание селекции, и
приложение не подмешает их CSS.
const grChartBulletConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrChartBullet">;Файлdist/types/components/GrChartBullet/defaults.d.ts
Пропы GrChartBullet, настраиваемые глобально через
<GrConfigProvider :component-defaults="{ GrChartBullet: { … } }">.
Только оформление: величина, цель и границы диапазонов задаются на месте.
interface GrChartBulletConfigurableProps {
size: GrChartSize;
height: number;
orientation: 'horizontal' | 'vertical';
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/GrChartBullet/safelist.d.ts
const grChartBulletSafelist: string[];Файлdist/types/components/GrChartBullet/GrChartBullet.vue.d.ts
Bullet Стивена Фью: величина, цель и качественные диапазоны в одну строку.
Три разных визуальных веса, чтобы они не спорили: диапазоны — фон, значение —
узкая полоса поверх, цель — засечка поперёк. Циферблата в пакете нет
намеренно: он тратит много места на мало данных и плохо читается
количественно.
Роль оверлея здесь meter, а не application: это не приложение со своей
картой клавиш, а величина на шкале. Роль исчезает вместе со значением —
meter без aria-valuenow невалиден.
interface GrChartBulletProps {
/** Измеряемая величина. `null` — величины нет; это не ноль. */
value: number | null;
/** Целевое значение — засечка поперёк дорожки. */
target?: number;
/**
* Границы качественных диапазонов: `[0.9, 1.0]` при `max: 1.2` даёт три
* полосы — до 0.9, 0.9…1.0 и свыше 1.0. Порядок не важен.
*/
ranges?: readonly number[];
/** Верх шкалы. Не задан — максимум из величины, цели и границ с запасом. */
max?: number;
/** Низ шкалы; по умолчанию 0. Полоса bullet без нуля искажает длину — величину читают по ней. */
min?: number;
/** Цвета полос от «хорошо» к «плохо». Длина на единицу больше `ranges`. */
rangeColors?: readonly string[];
/** Тон самой полосы значения. */
color?: string;
/** Направление дорожки: `horizontal` (по умолчанию) или `vertical`. */
orientation?: 'horizontal' | 'vertical';
/** Имя метрики: заголовок строки в таблице и в тултипе. */
label?: string;
/**
* Высота холста в пикселях; по умолчанию 48. Данные, загрузка и пустое состояние занимают её
* одинаково.
*/
height?: number;
/** Объявленная ширина: от неё идёт первый рендер, дальше ширина замеряется. */
width?: number;
/**
* Формат значений в тултипе, скрытой таблице и объявлениях: знаки после запятой и разделители.
*/
valueFormat?: GrChartNumberFormat;
/**
* Панель со значениями под курсором; по умолчанию `true`. `false` прячет только
* панель: курсор, активные марки, `update:activeIndex` и `pointHover` работают,
* пока график интерактивен.
*/
tooltip?: boolean;
/**
* Загрузка: на месте области построения — скелет той же высоты, место под оси уже
* зарезервировано.
*/
loading?: boolean;
/** Пустое состояние явно. Не задано — график пуст, когда рисовать нечего. */
empty?: boolean;
/** Текст заглушки пустого графика. Не задан — «Нет данных» из переводов пакета. */
emptyText?: string;
/**
* Таблица данных: `hidden` (по умолчанию) — только для скринридера, `visible` — под графиком,
* `off` — без неё.
*/
dataTable?: 'hidden' | 'visible' | 'off';
/** `false` — график становится картинкой: без фокуса, тултипа и клавиатуры. */
interactive?: boolean;
/** Кегль подписей по контрольной шкале ядра; по умолчанию — размер из `GrConfigProvider`. */
size?: GrChartSize;
/** BCP-47 локаль чисел и дат. Не задана — язык переводов приложения, иначе `en`. */
locale?: string;
/** Доступное имя графика. Не задано — сводка из `label`, величины и цели. */
ariaLabel?: string;
/**
* Описание для скринридера — вывод, ради которого график показан; дополняет сводку, а не заменяет
* её.
*/
ariaDescription?: string;
/**
* Потолок строк скрытой таблицы данных.
*
* `'auto'` (по умолчанию) — столько строк, сколько можно прочитать; выше
* потолка таблица идёт равномерной выборкой и говорит об этом пометкой, а
* поточечная полнота остаётся за клавиатурой. Число задаёт свой потолок,
* `Infinity` снимает его совсем, `dataTable: 'off'` убирает таблицу целиком —
* что из этого нужно, решает приложение.
*/
dataTableMaxRows?: number | 'auto';
}interface GrChartBulletEmits {
/** Щелчок или Enter по графику; в событии — сама величина. */
(e: 'valueClick', value: number | null): void;
}Использование из прозы библиотеки
Циферблата в пакете нет намеренно
Gauge тратит много места на мало данных и плохо читается количественно: со стрелки снимают «примерно посередине», а не число. Bullet решает ту же задачу полосой, а когда таких метрик несколько — ещё и сравнивается по вертикали, чего циферблаты не умеют вовсе.
Три веса, которые не спорят
Диапазоны — фон, значение — узкая полоса поверх, цель — засечка поперёк.
Толщина полосы значения задаётся токеном --gr-chart-bullet-value-width, а не
геометрией: полоса рисуется <line>, и тема меняет её вес, не трогая дорожку.
Свои цвета диапазонов задаёт rangeColors — по одному на полосу, от «хорошо» к
«плохо». Полос всегда на одну больше, чем границ, даже если граница вышла за
шкалу: такая граница зажимается, а не выбрасывается, иначе цвета уехали бы
на соседние полосы.
value: null — не ноль
Величины нет — полоса не рисуется, засечка цели остаётся, в таблице прочерк. Для себестоимости это буквальный случай: нет списаний — нет и себестоимости, и ноль здесь соврал бы.
Вместе со значением исчезает и роль meter: она требует aria-valuenow, а без
величины его нет — оставленная роль дала бы нарушение уровня serious.
Значение за шкалой не обрезается молча
Полоса упирается в край и получает маркер переполнения, а настоящая величина уходит в тултип, в скрытую таблицу и в объявление. Обрезать её тихо значило бы показать другое число.
Роль — meter, а не progressbar
progressbar — про выполнение задачи, которое заканчивается; здесь величина,
которая просто есть. slider не подходит тем более: значение не редактируется.
Помимо aria-valuenow/valuemin/valuemax оверлей несёт aria-valuetext с
человеческой формулировкой («0,031 из 0,05, цель 0,04») — одно число без единиц
и без цели сказало бы меньше, чем видит зрячий. Диапазоны объявляются в описании
графика: цветные зоны иначе существуют только для зрячих.
Границы
Нескольких значений в одной дорожке нет: bullet сравнивает одну величину с одной целью. Стрелку и «набор» значения анимацией компонент тоже не рисует.
Пропы этого примера