GrDelta
Берут, когда число со знаком стоит в предложении.
Обзор
Когда брать
- число со знаком стоит в предложении — в описании операции, в ячейке таблицы, в подписи под графиком;
- знак решает цвет — рост, падение и «не изменилось» видно до чтения самой величины;
- рост бывает плохим —
polarity="negative-good"для себестоимости, времени отклика и оттока; - величина может отсутствовать — «нет данных» и «ноль» рисуются по-разному.
Когда взять другое
| Нужно | Берите |
|---|---|
| Крупный показатель плиткой, с подписью и динамикой | GrStatistic |
| Число как статус или метка | GrBadge |
| Доля от целого полосой или кольцом | GrProgressBar / GrProgressCircle |
| Ряд значений во времени | GrSparkline |
| Пара «характеристика → значение» | GrDescriptionList |
Playground контролы выводятся из API, а не пишутся руками
Загружается…
0 изменено· состояние — в адресе
<GrDelta :value="12.4" />Примеры 4 у каждого — живое демо и его код
Изменение внутри фразы
На ступени по умолчанию своего кегля у дельты нет: внутри заголовка она крупная, внутри фразы — кеглем фразы, а стрелка растёт вместе с числом. Ноль — «не изменилось» и остаётся нейтральным.
Revenue in September +8,4%
Recurring revenue reached €48,920, +8,2% week over week. Trial conversion slipped by -1,2 pp, and seats per account stayed at 4.2 — 0%, which is neither good nor bad news.
Нет данных — это не ноль
nullпечатаетempty-textбез тона, стрелки и приписок — «€—» читалось бы как ноль евро. Знак ставитIntl, поэтомуlocaleдвигает разделители, а знак по-прежнему встаёт перед валютой.- Germany+12,6%
- Poland0,0%
- Sweden-3,4%
- Portugalnew market
en-GB+€1,284.50de-DE+1.284,50 €fr-FR+1 284,50 €
Когда рост — плохая новость
polarity="negative-good"переворачивает тон для себестоимости, задержки и оттока, но не знак: упавшая себестоимость всё ещё −€0.42, и она зелёная.noneснимает тон там, где знак не оценка.Metric This month Change polarityRevenue €92,350 +6,3% positive-good· more is betterCost per order €3.18 -€0,42 negative-good· less is betterLatency, p95 412 ms +38 ms negative-good· less is betterMonthly churn 2.1 % +0,4 pp negative-good· less is betterCash balance shift €18,400 -€1 240 none· the sign is not a verdictСтупени для ряда контролов, хуки для остального
Рядом с контролами
size="sm"совпадает с ними, а не с текстом вокруг.--gr-delta-suffix-colorи--gr-delta-arrow-sizeприглушают единицу или увеличивают стрелку, а публичнаяdeltaToneдаёт бейджу ту же оценку, что и дельте.Orders +4,6% Refunds +0,3 ppLatency -12msFaster than last week
API порождается из пакета · разойтись не может
Props
| Prop | Type | по умолчанию | Описание |
|---|---|---|---|
size | "xs" | "sm" | "md" | "lg" | undefined | undefined | Кегль по шкале контролов. По умолчанию `md` — кегль окружающей строки; `xs`, `sm`, `lg` нужны, когда величина стоит в ряду с контролами. |
emptyText | string | undefined | undefined | Чем печатать отсутствующую величину. |
locale | string | undefined | undefined | Локаль форматирования. Не задана — локаль i18n-адаптера. |
precision | number | undefined | undefined | Знаков после запятой. Не задано — как отдаст локаль. |
prefix | string | undefined | undefined | Приписка перед числом: валюта, единица. |
suffix | string | undefined | undefined | Приписка после числа: `%`, `мс`. |
polarity | GrDeltaPolarity | undefined | undefined | Что считать хорошим. Для выручки рост — успех, для себестоимости и времени отклика — наоборот; без этого компонент врал бы в половине случаев. |
showSign | boolean | undefined | undefined | Ставить `+` у положительных. Минус подставляет `Intl` сам. |
showArrow | boolean | undefined | undefined | Стрелка направления. Декоративна: направление уже в знаке. |
valueобязательный | number | null | — | Величина. `null` — её нет; это не ноль. |
Типы
Все экспортируемые типы подпути, по файлам, где они объявлены. Имена, дженерики и тела читаются из файлов деклараций пакета и разойтись не могут.
import type { GrDeltaPolarity, GrDeltaTone, GrDeltaProps } from '@feugene/granularity/components/GrDelta'Файлdist/types/components/GrDelta/config.d.ts
const grDeltaConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrDelta">;Файлdist/types/components/shared/deltaTone.d.ts
Что считать хорошим ростом.
type GrDeltaPolarity = 'positive-good'type GrDeltaTone = 'success' | 'danger' | 'neutral';Тон величины по её знаку и полярности.
Ноль нейтрален при любой полярности: «не изменилось» — третье состояние, и
двумя цветами его не выразить. null величины нет вовсе — тона у неё тоже
нет, это решает вызывающий.
function deltaTone(value: number, polarity?: GrDeltaPolarity): GrDeltaTone;Направление стрелки. Ноль стрелки не получает: двигаться было некуда.
function deltaDirection(value: number): 'up' | 'down' | 'flat';Файлdist/types/components/GrDelta/GrDelta.vue.d.ts
Величина со знаком и тоном **внутри строки текста**: «Дельта: +$0.004».
Компонент презентационный: дельту не считает и периоды не сравнивает —
«к чему сравнивать» знает приложение. Блочная плитка показателя — GrStatistic.
interface GrDeltaProps {
/** Величина. `null` — её нет; это не ноль. */
value: number | null;
/** Знаков после запятой. Не задано — как отдаст локаль. */
precision?: number;
/** Приписка перед числом: валюта, единица. */
prefix?: string;
/** Приписка после числа: `%`, `мс`. */
suffix?: string;
/**
* Что считать хорошим. Для выручки рост — успех, для себестоимости и времени
* отклика — наоборот; без этого компонент врал бы в половине случаев.
*/
polarity?: GrDeltaPolarity;
/** Ставить `+` у положительных. Минус подставляет `Intl` сам. */
showSign?: boolean;
/** Стрелка направления. Декоративна: направление уже в знаке. */
showArrow?: boolean;
/** Чем печатать отсутствующую величину. */
emptyText?: string;
/** Локаль форматирования. Не задана — локаль i18n-адаптера. */
locale?: string;
/**
* Кегль по шкале контролов. По умолчанию `md` — кегль окружающей строки; `xs`, `sm`, `lg`
* нужны, когда величина стоит в ряду с контролами.
*/
size?: GrComponentSize;
}Файлdist/types/components/GrDelta/defaults.d.ts
Пропы GrDelta, настраиваемые глобально через
<GrConfigProvider :component-defaults="{ GrDelta: { … } }">.
polarity здесь наравне с оформлением не случайно: в отчёте по расходам
«рост — плохо» верно для всех величин сразу, и повторять это на каждой
значит однажды забыть.
interface GrDeltaConfigurableProps {
size: GrComponentSize;
polarity: GrDeltaPolarity;
showSign: boolean;
showArrow: boolean;
emptyText: string;
}Файлdist/types/components/GrDelta/safelist.d.ts
const grDeltaSafelist: string[];Использование из прозы библиотеки
Ноль нейтрален, null — это не ноль
Два состояния, которые двумя цветами не выражаются, и оба уже приводили к ошибкам в приложениях:
- ноль — «не изменилось». Ни успех, ни ошибка: тон нейтральный при любой
polarity. Условие видаvalue < 0 ? danger : successкрасит нулевое движение зелёным, как поступление; null— величины нет вовсе. Печатается прочерком (emptyText), без тона, без стрелки и без приписок:$—читается как «ноль долларов».
Выбор тона живёт отдельной чистой функцией и покрыт тестами без монтирования —
именно этот switch расходился копиями по приложению.
Функция публичная: deltaTone(value, polarity) и deltaDirection(value)
отдаются из пакета. Они нужны там, где разметка дельты не подходит, а правило
то же — tone у GrStatistic и GrBadge в
плитках отчёта. Переписанное в приложении, оно расходится с этим при первой же
правке — что и произошло с нулём, покрашенным в зелёный как поступление.
Полярность инвертирует тон, но не знак
<GrDelta :value="-15" suffix="%" polarity="negative-good" />Себестоимость упала на 15 % — это успех, и величина зелёная. Но она всё ещё
−15 %: знак принадлежит числу, а не оценке. Стрелка тоже идёт за знаком —
вниз, потому что величина уменьшилась.
polarity="none" снимает тон совсем: у сальдо и смещения знак ничего не
говорит о качестве.
Знак ставит Intl, а не склейка строк
showSign включает signDisplay: 'exceptZero' в форматировании, а не
дописывает '+' к готовой строке. Разница не стилистическая: склейка уже
ломала GrStatistic — строка '+1,234' перестаёт быть числом, и разряды
теряются на следующем шаге.
Локаль берётся из i18n-адаптера, если не задана пропом.
Знак стоит перед валютой
Вместе с prefix знак уезжает в отдельный узел перед припиской: +$0,0280,
а не $+0,0280. Знак относится к величине целиком, а не к числу после символа
валюты, и второй порядок не принят ни в одной типографской традиции.
Инвариант выше при этом не ослаблен: знак по-прежнему ставит Intl, компонент
лишь вынимает его из formatToParts — строка не склеивается и разряды не
пересобираются. Без prefix выносить некуда, и разметка остаётся прежней.
Настраивать положение знака нечем намеренно. Отдельная тонкость — RTL: там знаку предшествует невидимая метка направления, которой он и разворачивается относительно цифр; она уезжает вместе со знаком, потому что управляет именно им.
Сами приписки рисует GrValue — общий примитив с
GrStatistic. Оттуда же их оформление и токены --gr-value-*: валюта слева
набирается как число, единица справа приглушается. Валюта справа (+1 000 ₽)
получается сменой дефолта суффикса, рецепт — на странице примитива.
Свои точки настройки
У дельты четыре собственных хука (--gr-delta-*, полный список — в
../tokens.md), и появились они не ради симметрии.
Приписку дельта не приглушает: «−15 %» набирается кеглем и цветом строки
целиком, иначе процент читается подписью при числе. Это перекрытие дефолта
GrValue, и оно применяется инлайн-стилем — а инлайн бьёт любой CSS
потребителя. Задай компонент цвет литералом, и вернуть приглушение стало бы
нечем совсем; поэтому перекрытие ссылается на --gr-delta-suffix-color и
--gr-delta-suffix-size, оставляя решение тому, кто их задал:
<GrDelta :value="-15" suffix="%" style="--gr-delta-suffix-color: var(--gr-muted-fg)" />Стрелка настраивается --gr-delta-arrow-size и --gr-delta-arrow-gap. Размер —
доля от кегля величины, а не ступень шкалы: на md своего кегля у дельты нет
вовсе, и пиксельное значение оставило бы стрелку мелкой внутри заголовка.
Отбивка логическая, поэтому в RTL встаёт с нужной стороны.
Кегль приходит из строки
Ступень по умолчанию (md) font-size не задаёт вовсе: величина набирается
кеглем той строки, в которой стоит. Внутри заголовка она крупная, в подписи под
графиком мелкая, в ячейке таблицы — ровно как остальной текст ячейки, и всё это
без единого пропа.
<h2 class="text-[length:var(--gr-text-3xl)]">
Выручка за март <GrDelta :value="8.4" :precision="1" suffix="%" show-arrow />
</h2>Края лестницы остаются явными и берут контрольную шкалу — xs 12 px, sm 13 px,
lg 16 px. Они нужны в другом случае: когда величина стоит не в предложении, а
в ряду с контролами, и обязана совпасть с ними, а не с окружающим текстом.
Стрелка задана в em и растёт вместе с числом — своей ступени у неё нет.
Переопределяя кегль снаружи, переопределяйте пару: межстрочный
компонент не трогает, и половинчатая правка оставит строку с чужим интервалом.
Стрелка декоративна
showArrow рисует направление, но помечает иконку aria-hidden: направление
уже объявлено знаком, и дублировать его для скринридера незачем. По умолчанию
стрелки нет — в строке текста она чаще шумит, чем помогает.
Границы
Дельту компонент не считает: принимает готовую. «К чему сравнивать» — период, базовое значение, фильтры — знает приложение, и втаскивать это в презентационный компонент значит тащить туда же его модель данных. Проценты, сравнение периодов и спарклайн — тоже снаружи.
Пропы этого примера