GrChartLine

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

Берут, когда как величина менялась.

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

Обзор

Когда брать

  • как величина менялась — выручка по месяцам, нагрузка по часам, остаток по дням: линия показывает ход, а не отдельные замеры;
  • несколько рядов рядом — легенда переключает серии, тултип показывает все значения в одной абсциссе;
  • в ряду есть пропуски — null рвёт линию, а не подставляет ноль; gaps рисует через провал перемычку, когда важнее читаемость тренда;
  • точек тысячи — decimate: 'auto' (по умолчанию) сокращает рисунок до двух вершин на пиксель, а курсор, клавиатура и скрытая таблица продолжают знать полный ряд. Разбор — ../model.md, раздел «Прореживание — проекция, а не данные».

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

НужноБерите
Показать целое и вклад частей в негоGrChartArea
Сравнить величины по категориямGrChartBar
Показать, из чего состоит одно целоеGrChartPie
Сравнить форму профиля по нескольким осямGrChartRadar
Тренд в ячейке таблицы, без осей и рамыGrSparkline
Одно число крупно, с динамикойGrStatistic

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

  • Месяц задержек и час, который его сломал

    720 точек на серию на шкале времени: рисунок прорежен до ширины, а курсор и скрытая таблица знают каждый час. v-model:x-window — видимое окно: его ставят пресеты, zoom="brush" даёт читателю протянуть своё, а ось переходит с дней на часы, когда окно становится коротким. SLO — линия references, а не серия.

    Checkout API · latencyLast 30 days · drag over the chart to zoom in
    Данные графика
    Xp95p50
    7 сент. 2026 г., 00:0015358
    7 сент. 2026 г., 07:0013166
    7 сент. 2026 г., 13:0023280
    7 сент. 2026 г., 22:0014766
    8 сент. 2026 г., 06:0014666
    8 сент. 2026 г., 11:0022880
    8 сент. 2026 г., 17:0022981
    8 сент. 2026 г., 19:0016977
    9 сент. 2026 г., 06:0013864
    9 сент. 2026 г., 11:0021878
    9 сент. 2026 г., 14:0025482
    9 сент. 2026 г., 22:0014564
    10 сент. 2026 г., 07:0015259
    10 сент. 2026 г., 13:0023991
    10 сент. 2026 г., 15:0024484
    10 сент. 2026 г., 21:0016557
    11 сент. 2026 г., 07:0013968
    11 сент. 2026 г., 12:0023979
    11 сент. 2026 г., 17:0023481
    11 сент. 2026 г., 21:0013862
    12 сент. 2026 г., 07:0012866
    12 сент. 2026 г., 10:0017675
    12 сент. 2026 г., 14:0020670
    12 сент. 2026 г., 22:0012758
    13 сент. 2026 г., 06:0012859
    13 сент. 2026 г., 13:0020770
    13 сент. 2026 г., 15:0016576
    14 сент. 2026 г., 00:0013963
    14 сент. 2026 г., 06:0013561
    14 сент. 2026 г., 13:0024980
    14 сент. 2026 г., 19:0019966
    14 сент. 2026 г., 22:0012866
    15 сент. 2026 г., 07:0013461
    15 сент. 2026 г., 13:0024986
    15 сент. 2026 г., 16:0024480
    16 сент. 2026 г., 00:0013361
    16 сент. 2026 г., 07:0013764
    16 сент. 2026 г., 12:0024384
    16 сент. 2026 г., 21:0013868
    16 сент. 2026 г., 23:0017260
    17 сент. 2026 г., 05:0013365
    17 сент. 2026 г., 11:0023180
    17 сент. 2026 г., 21:0014856
    18 сент. 2026 г., 02:0013868
    18 сент. 2026 г., 08:0015167
    18 сент. 2026 г., 14:0025388
    18 сент. 2026 г., 21:0015058
    19 сент. 2026 г., 01:0017064
    19 сент. 2026 г., 05:0013668
    19 сент. 2026 г., 11:0020073
    19 сент. 2026 г., 19:0015473
    20 сент. 2026 г., 04:0012965
    20 сент. 2026 г., 05:0016462
    20 сент. 2026 г., 14:0020472
    20 сент. 2026 г., 22:0013767
    21 сент. 2026 г., 04:0012864
    21 сент. 2026 г., 10:0021877
    21 сент. 2026 г., 16:0024084
    21 сент. 2026 г., 22:0012860
    22 сент. 2026 г., 00:0016463
    22 сент. 2026 г., 06:0015067
    22 сент. 2026 г., 13:0025081
    22 сент. 2026 г., 21:0014866
    23 сент. 2026 г., 00:0013869
    23 сент. 2026 г., 07:0013568
    23 сент. 2026 г., 15:0025288
    23 сент. 2026 г., 21:0014263
    24 сент. 2026 г., 04:0013561
    24 сент. 2026 г., 07:00629177
    24 сент. 2026 г., 12:0023977
    24 сент. 2026 г., 21:0012859
    25 сент. 2026 г., 05:0012959
    25 сент. 2026 г., 12:0023879
    25 сент. 2026 г., 16:0024887
    25 сент. 2026 г., 23:0013367
    26 сент. 2026 г., 04:0016959
    26 сент. 2026 г., 07:0013568
    26 сент. 2026 г., 13:0020971
    26 сент. 2026 г., 21:0013964
    27 сент. 2026 г., 06:0013762
    27 сент. 2026 г., 12:0020267
    27 сент. 2026 г., 14:0017171
    27 сент. 2026 г., 22:0013562
    28 сент. 2026 г., 07:0013561
    28 сент. 2026 г., 13:0024091
    28 сент. 2026 г., 15:0024780
    28 сент. 2026 г., 20:0014866
    29 сент. 2026 г., 07:0014867
    29 сент. 2026 г., 11:0023177
    29 сент. 2026 г., 14:0025789
    29 сент. 2026 г., 22:0013967
    30 сент. 2026 г., 07:0014565
    30 сент. 2026 г., 11:0023076
    30 сент. 2026 г., 15:0024781
    30 сент. 2026 г., 22:0014168
    1 окт. 2026 г., 06:0013466
    1 окт. 2026 г., 13:0023581
    1 окт. 2026 г., 20:0014972
    1 окт. 2026 г., 21:0017264
    2 окт. 2026 г., 07:0013562
    2 окт. 2026 г., 11:0023081
    2 окт. 2026 г., 15:0025082
    2 окт. 2026 г., 21:0012865
    3 окт. 2026 г., 08:0018066
    3 окт. 2026 г., 10:0015672
    3 окт. 2026 г., 17:0018970
    3 окт. 2026 г., 21:0014156
    4 окт. 2026 г., 07:0013366
    4 окт. 2026 г., 10:0019269
    4 окт. 2026 г., 19:0017765
    4 окт. 2026 г., 22:0012960
    5 окт. 2026 г., 06:0014064
    5 окт. 2026 г., 14:0024681
    5 окт. 2026 г., 21:0013061
    6 окт. 2026 г., 02:0017164
    6 окт. 2026 г., 07:0014356
    6 окт. 2026 г., 15:0025388
    6 окт. 2026 г., 18:0018781
    6 окт. 2026 г., 22:0016857
    6 окт. 2026 г., 23:0014657
    SLO 300 ms: 300
    Показано 120 из 720 точек — те же, что нарисованы. Стрелками доступны все.
  • Датчик, который замолчал

    null рвёт линию, а не роняет её в ноль: замолчавший датчик не намерил 0 °C. gaps="dashed" перекидывает через провал перемычку, чтобы тренд читался дальше, а пунктир говорит, что это не замер. Допустимый коридор — опорная полоса, а curve="smooth" не выносит кривую за показания.

    Fridge 3 · temperatureNo readings 03:00–05:00 — logger offline
    Данные графика
    XFridge 3
    6 окт. 2026 г., 00:004,6
    6 окт. 2026 г., 00:304,8
    6 окт. 2026 г., 01:005,1
    6 окт. 2026 г., 01:305,0
    6 окт. 2026 г., 02:004,9
    6 окт. 2026 г., 02:305,3
    6 окт. 2026 г., 03:00нет значения
    6 окт. 2026 г., 03:30нет значения
    6 окт. 2026 г., 04:00нет значения
    6 окт. 2026 г., 04:30нет значения
    6 окт. 2026 г., 05:007,6
    6 окт. 2026 г., 05:306,9
    6 окт. 2026 г., 06:006,1
    6 окт. 2026 г., 06:305,4
    6 окт. 2026 г., 07:005,0
    6 окт. 2026 г., 07:304,8
    6 окт. 2026 г., 08:004,7
    6 окт. 2026 г., 08:304,9
    6 окт. 2026 г., 09:005,2
    6 окт. 2026 г., 09:305,1
    6 окт. 2026 г., 10:004,8
    6 окт. 2026 г., 10:304,6
    6 окт. 2026 г., 11:004,5
    6 окт. 2026 г., 11:304,7
    6 окт. 2026 г., 12:004,8
    2–8 °C: от 2,0 до 8,0
  • Места, которые меняются в день, а не плавно

    curve="step" рисует величину, которая скачет и держится, — наклонная линия выдумала бы 18,5 места в марте. show-points="always" отмечает выставленные месяцы, а point-click открывает строку счёта за месяц.

    Northwind Traders · seats billedTeam plan, €12 a seat · click a month
    Данные графика
    XSeats
    1 нояб. 2025 г., 00:0012
    1 дек. 2025 г., 00:0012
    1 янв. 2026 г., 00:0015
    1 февр. 2026 г., 00:0015
    1 мар. 2026 г., 00:0015
    1 апр. 2026 г., 00:0022
    1 мая 2026 г., 00:0022
    1 июн. 2026 г., 00:0022
    1 июл. 2026 г., 00:0020
    1 авг. 2026 г., 00:0028
    1 сент. 2026 г., 00:0028
    1 окт. 2026 г., 00:0034

    Click a month to see its invoice line.

  • Две единицы — две шкалы

    dual-axis даёт конверсии свою шкалу справа — на левой она легла бы плашмя под 1 700 регистраций. У каждой оси свой формат делений, опора по x отмечает смену цен, а v-model:hidden-series держит состояние легенды у страницы: график только сообщает намерение.

    Growth after the price changeMore people sign up since W32, fewer of them pay
    Данные графика
    XSign-ups (левая ось)Paid within 14 days (правая ось)
    W288206,1
    W298606,0
    W309106,3
    W318706,4
    W321 0405,8
    W331 1805,2
    W341 2105,0
    W351 1605,1
    W361 3904,6
    W371 5204,3
    W381 4804,4
    W391 6104,1
    W401 7003,9
    New prices: W32

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

Props

PropTypeпо умолчаниюОписание
xScaleGrChartScaleKind | undefinedundefinedТип оси X. Не задан — выводится из данных.
heightnumber | undefinedundefinedВысота холста в пикселях; по умолчанию 256. Данные, загрузка и пустое состояние занимают её одинаково.
widthnumber | undefined640Объявленная ширина: от неё идёт первый рендер, дальше ширина замеряется.
yDomainreadonly [number | null, number | null] | undefinedundefinedГраницы оси значений `[min, max]`; `null` на месте границы — она берётся по данным.
includeZeroboolean | undefinedfalseДотянуть ось значений до нуля; по умолчанию нет — ось облегает данные.
referencesreadonly GrChartReference[] | undefinedundefinedОпорные линии и полосы: порог, план, коридор допустимого. Не серия и нигде ею не считается: в легенду не попадает, индекс палитры не тратит, в стек не входит и в скрытую таблицу уезжает примечанием, а не строкой данных.
includeReferencesInDomainboolean | undefinedfalseВключить опоры в домен оси. По умолчанию нет, и это осознанно: порог `1.0` при данных около `0.03` растянул бы ось так, что сами данные схлопнулись бы в линию.
dualAxisboolean | undefinedfalseПоказывать вторую ось значений справа. Без неё серии с `axis: 'right'` попадают на левую. Включение осознанное: две оси позволяют подогнать любые два ряда под видимую корреляцию, и это должно быть решением автора графика, а не побочным эффектом поля в данных.
yDomainRightreadonly [number | null, number | null] | undefinedundefinedГраницы правой оси значений при `dualAxis`; `null` на месте границы — она берётся по данным.
yTickFormatRight((value: number) => string) | undefinedundefinedПодпись деления правой оси. Не задана — число по локали.
valueFormatRightGrChartNumberFormat | undefinedundefinedФормат значений серий правой оси в тултипе, скрытой таблице и объявлениях.
xTickCountnumber | undefinedundefinedЖелаемое число делений оси X; по умолчанию 6. У категорий — потолок числа подписей (не задан — 12). Подписи, которые не помещаются по ширине, прореживаются.
yTickCountnumber | undefined5Желаемое число делений оси значений; по умолчанию 5. Шаг остаётся «круглым», поэтому делений бывает на одно меньше.
xTickFormatChartTickFormat | undefinedundefinedПодпись деления оси X: `(value, kind) => string`. Не задана — категория, дата или число по локали.

Слоты

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

События

EventСигнатураОписание
update:hiddenSeries(event: "update:hiddenSeries", value: string[]): void—
update:activeIndex(event: "update:activeIndex", value: number | null): void—
update:xWindow(event: "update:xWindow", value: GrChartXWindow | null): void—
pointClick(event: "pointClick", value: GrChartActivePoint): void—
pointHover(event: "pointHover", value: GrChartActivePoint | null): void—
legendToggle(event: "legendToggle", value: { seriesId: string; hidden: boolean; }): void—

Методы

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

Типы

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

Импорт
import type { GrChartLineConfigurableProps, GrChartSize, GrChartLineProps } from '@feugene/granularity-charts/components/GrChartLine'

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

grChartLineConfigconst

GrEmptyState и GrSkeleton объявлены зависимостями потому, что рама их **рендерит**: без объявления они не попадут в замыкание селекции, и потребитель получит пустое состояние без стилей и скелет без фона. group помечает, что рама у графиков общая: шаблоны живут в src/components/GrChartFrame/shared/ и уезжают в общий чанк, который входит в файлы каждого дотянувшегося компонента. Ребром графа он не становится — иначе вынесенный хелпер превращал бы соседей в зависимости друг друга, — а классы из него сборка извлекает по графу бандла, не требуя перечисления в safelist. Ту же группу получают остальные графики: рама у них общая, ради этого механизм и существует.

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

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

GrChartLineConfigurablePropsinterface

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

interface GrChartLineConfigurableProps {
    /**
     * Когда прореживать ряд для рисунка. Политика рендера — приложение с плотными
     * рядами задаёт её один раз на всё поддерево.
     */
    decimate: 'auto' | 'always' | 'never';
    /** Бюджет точек на серию. */
    maxPoints: number;
    /**
     * Какими жестами меняется видимое окно.
     *
     * Политика приложения, а не свойство отдельного графика: включать
     * приближение на одной панели и не включать на соседней — это разные правила
     * поведения у одинаковых с виду картинок.
     */
    zoom: GrChartZoom;
    /**
     * Потолок строк скрытой таблицы данных.
     *
     * Политика приложения: держать ли полный ряд в дереве доступности, усечь ли
     * его до нарисованного или убрать таблицу вовсе — решение уровня продукта, а
     * не отдельного графика.
     */
    dataTableMaxRows: number | 'auto';
    size: GrChartSize;
    height: number;
    curve: GrChartCurve;
    /** Чем закрыть разрыв ряда: ничем, тенью или штрихом. */
    gaps: 'hidden' | 'shadow' | 'dashed';
    showGrid: 'both' | 'x' | 'y' | 'none';
    showLegend: boolean | 'auto';
    legendPosition: 'top' | 'bottom';
    tooltip: boolean;
    dataTable: 'hidden' | 'visible' | 'off';
}

Файл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/GrChartLine/safelist.d.ts

grChartLineSafelistconst
const grChartLineSafelist: string[];

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

GrChartLinePropsinterface

Линейный график: ряд во времени, по числовой оси или по категориям. Рисует только марки — оси, сетку, легенду, тултип, состояния, клавиатуру и скрытую таблицу данных берёт на себя общая рама.

interface GrChartLineProps {
    /** Серии либо голый ряд чисел — тогда `x` становится порядковым номером. */
    series: readonly GrChartSeries[] | readonly number[];
    /** Тип оси X. Не задан — выводится из данных. */
    xScale?: GrChartScaleKind;
    /**
     * Высота холста в пикселях; по умолчанию 256. Данные, загрузка и пустое состояние занимают её
     * одинаково.
     */
    height?: number;
    /** Объявленная ширина: от неё идёт первый рендер, дальше ширина замеряется. */
    width?: number;
    /** Границы оси значений `[min, max]`; `null` на месте границы — она берётся по данным. */
    yDomain?: readonly [number | null, number | null];
    /** Дотянуть ось значений до нуля; по умолчанию нет — ось облегает данные. */
    includeZero?: boolean;
    /**
     * Опорные линии и полосы: порог, план, коридор допустимого.
     *
     * Не серия и нигде ею не считается: в легенду не попадает, индекс палитры не
     * тратит, в стек не входит и в скрытую таблицу уезжает примечанием, а не
     * строкой данных.
     */
    references?: readonly GrChartReference[];
    /**
     * Включить опоры в домен оси.
     *
     * По умолчанию нет, и это осознанно: порог `1.0` при данных около `0.03`
     * растянул бы ось так, что сами данные схлопнулись бы в линию.
     */
    includeReferencesInDomain?: boolean;
    /**
     * Показывать вторую ось значений справа.
     *
     * Без неё серии с `axis: 'right'` попадают на левую. Включение осознанное:
     * две оси позволяют подогнать любые два ряда под видимую корреляцию, и это
     * должно быть решением автора графика, а не побочным эффектом поля в данных.
     */
    dualAxis?: boolean;
    /**
     * Границы правой оси значений при `dualAxis`; `null` на месте границы — она берётся по данным.
     */
    yDomainRight?: readonly [number | null, number | null];
    /** Подпись деления правой оси. Не задана — число по локали. */
    yTickFormatRight?: (value: number) => string;
GrChartLineEmitsinterface
interface GrChartLineEmits {
    /**
     * Легенда просит скрыть или показать серию — новый список id; применяет его потребитель
     * (`v-model:hiddenSeries`).
     */
    (e: 'update:hiddenSeries', value: string[]): void;
    /** Курсор сдвинулся: индекс позиции или `null` — для `v-model:activeIndex`. */
    (e: 'update:activeIndex', value: number | null): void;
    /** Жест приближения сменил видимое окно — для `v-model:xWindow`. */
    (e: 'update:xWindow', value: GrChartXWindow | null): void;
    /** Щелчок или Enter по активной позиции — со значениями всех видимых серий в ней. */
    (e: 'pointClick', value: GrChartActivePoint): void;
    /** Курсор пришёл на позицию или ушёл с графика (`null`). */
    (e: 'pointHover', value: GrChartActivePoint | null): void;
    /** Нажат пункт легенды: какая серия и в какое состояние просится. */
    (e: 'legendToggle', value: {
        seriesId: string;
        hidden: boolean;
    }): void;
}

Доступность

Паттерн APG

При нескольких сериях ↑/↓ переключают читаемую серию

КлавишаДействие
—точка ряда по оси X

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

Сглаживание не изобретает значений

curve: 'smooth' — монотонная кубика: она не выбрасывает кривую за диапазон соседних значений. Обычный кубический сплайн на резком перепаде даёт «горб» ниже минимума или выше максимума пары — то есть рисует значение, которого в данных нет. step уместен там, где величина и правда менялась ступенями: тариф, версия, статус.

Перемычка gaps всегда прямая, даже при smooth. Кривая через пропуск имела бы форму, то есть показывала бы ход значения там, где его не измеряли.

Марки появляются сами

showPoints: 'auto' рисует марки, пока ряд короткий (до шестидесяти точек). На длинном ряду они сливаются в сплошную полосу и мешают читать линию, поэтому пропадают. 'always' на пятидесяти тысячах точек — осознанный выбор потребителя, и стоит он ровно столько, сколько стоит.

Выше порога рисует холст

Тело графика умеет рисоваться двумя способами. Ниже порога это SVG, выше — <canvas>; выбирает сам компонент, пропа «каким рендерером» нет.

Порог считается в нарисованных вершинах, а не в точках — canvasThreshold, по умолчанию 24 000. Разница существенная: прореживание режет каждый ряд до предела экрана (около двух вершин на пиксель), поэтому один ряд в сто тысяч точек рисуется как 2400 вершин и стоит миллисекунды, а двадцать рядов по 2400 — те же 48 000 точек — стоят шестнадцати, то есть целого кадра.

Числа замера при ширине 1200px, 2400 вершин на ряд:

РядовSVGХолст
11,1 мс0,4 мс
86,7 мс0,6 мс
2016,3 мс1,7 мс

canvasThreshold: 0 выключает холст совсем — на случай, когда рисунок обязан остаться векторным: печать, экспорт SVG, свой CSS поверх марок.

Доступность от смены рендерера не меняется вовсе. Курсор, клавиатура, тултип и скрытая таблица работают с оверлеем и с полными рядами, а не с марками: холст для них не существует, он aria-hidden и не ловит указатель. Это и было условием, при котором второй рендерер вообще допустили.

Что холст рисует иначе. Сетка переезжает в него же: холст лежит под <svg>, чтобы оси и активная точка остались сверху, — а сетка обязана остаться под рядами. У площади градиентная заливка становится сплошной: url(#…) холст не понимает, а на двадцати площадях градиент всё равно читается как каша.

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

activeIndex — v-model: им синхронизируют пару графиков, чтобы курсор в одном подсвечивал ту же абсциссу в другом. hiddenSeries — тоже v-model, но компонент его не применяет сам: легенда излучает намерение, состояние принадлежит потребителю.

tooltip: false прячет только панель. Курсор под указателем и с клавиатуры, активные марки, update:activeIndex и pointHover остаются: у второго графика пары панель часто лишняя, а курсор нужен — он и показывает ту же абсциссу.

interactive: false превращает график в картинку: role="img" с именем, без фокуса, тултипа и клавиатуры. Это режим для печати и для плитки, по которой кликают целиком.

Скрытая таблица — не запасной вариант

dataTable: 'hidden' (по умолчанию) держит полные данные таблицей в дереве доступности, но не на экране. Скринридер читает значения, а не «график». 'visible' показывает её же под холстом, 'off' убирает совсем — и это осознанный отказ от доступности данных, а не оптимизация.

Приближение по абсциссе

zoom включает окно: 'brush' — протяжка по холсту, 'wheel' — колесо, 'both' — оба. По умолчанию выключено.

vue
<GrChartLine v-model:x-window="window" :series="series" zoom="both" />

<GrButton :disabled="window === null" @click="window = null">
Весь ряд
</GrButton>

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

Клавиатура работает всегда, когда включён zoom: +/- приближают к активной точке, Shift+стрелки сдвигают окно, 0 возвращает весь ряд. Union пропа перечисляет только жесты указателя — отключаемой клавиатуры у приближения нет by design (../a11y.md, раздел про приближение).

v-model:x-window не обязателен — без привязки график приближается сам. Привязывают его ради другого: синхронной пары графиков, кнопки сброса рядом с холстом, сохранения приближения в адресной строке.

Границы принимаются в том же виде, что абсциссы точек (Date, ISO-строка, число), наружу уходят числами. activeIndex при заданном окне адресует его, а не весь ряд.

Скрытая таблица и её потолок

Полные данные графика строками — то, что читает диктор вместо картинки. По умолчанию dataTable: 'hidden': в дереве доступности, глазами не видна.

Строка на точку читаема, пока строк немного. Поэтому у таблицы есть потолок — dataTableMaxRows, по умолчанию 'auto', то есть бюджет рисунка:

vue
<GrChartLine :series="series" />                              <!-- auto: как нарисовано -->

<GrChartLine :series="series" :data-table-max-rows="200" />   <!-- свой потолок -->

<GrChartLine :series="series" :data-table-max-rows="Infinity" /> <!-- всегда полная -->

<GrChartLine :series="series" data-table="off" />             <!-- таблицы нет вовсе -->

Выше потолка таблица печатает те же точки, что нарисованы, и говорит об этом пометкой в подвале. Поточечная полнота при этом не теряется: стрелки обходят весь ряд и проговаривают каждую точку. Таблица отвечает за обзор, клавиатура — за точное значение; разбор — ../model.md.

'auto' — это «столько строк, сколько можно прочитать». Бюджет рисунка берётся, когда он есть; при decimate: 'never' его нет, и остаётся фиксированный потолок с равномерной выборкой.

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

Порог рисуется опорой, а не серией

План, норма и граница допустимого — это проп references, а не ряд из константы: серия-константа попала бы в легенду, растянула бы ось и уехала бы в таблицу как данные. Разбор — ../model.md, раздел «Опора — не серия».

Две оси — осознанное решение

Ряды разного порядка (деньги и штуки) читаются на одном графике только с двумя осями, а две оси позволяют подогнать любую пару под видимую корреляцию. Поэтому axis: 'right' у серии не работает, пока не включён dualAxis. Инварианты — ../model.md, раздел «Вторая ось значений».