Skip to content

Легенда

Включена по умолчанию, снизу. Клик по элементу скрывает/показывает серию (toggleSeries).

Модульная сборка

В сборке через grafit-charts/core легенда — отдельный модуль: register(legendModule).

ts
import { getData } from './data';
import type { ChartOptions } from 'grafit-charts';

export function createOptions(): ChartOptions {
  return {
    data: getData(),
    title: { text: 'Team velocity' },
    series: [
      { type: 'bar', xField: 'sprint', yField: 'done', name: 'Done', stacked: true },
      { type: 'bar', xField: 'sprint', yField: 'carry', name: 'Carried over', stacked: true },
    ],
    // legend on the right; clicking an item hides the series
    legend: { position: 'right' },
  };
}
ts
export function getData() {
  return [
    { sprint: 'S1', done: 21, carry: 4 },
    { sprint: 'S2', done: 25, carry: 6 },
    { sprint: 'S3', done: 19, carry: 3 },
    { sprint: 'S4', done: 28, carry: 5 },
    { sprint: 'S5', done: 31, carry: 2 },
  ];
}

Опции

ОпцияТипПо умолчаниюОписание
enabledbooleantrueпоказать легенду
positionLegendPlacement'bottom'сторона + выравнивание или плавающий якорь (ниже)
floatingbooleanfalseповерх всей области чарта, без резервирования места
offset{ x?: Pixels; y?: Pixels }0только floating: отступ от закреплённых краёв
avoidCaptionsbooleantrueтолько floating: заголовки обтекают бокс легенды
toggleSeriesbooleantrueклик переключает видимость
maxRowsnumber2строк на страницу у горизонтальной легенды
maxWidthLengthразмер чартапредел ширины легенды (ниже)
maxHeightLengthразмер чартапредел высоты легенды (ниже)
reversebooleanfalseэлементы в обратном порядке
item.markerLegendMarkerOptionsфигура маркера (ниже)
item.label.fontSizePixels12шрифт подписи
item.label.fontFamilystringшрифт темыгарнитура
item.label.colorColorValueforegroundцвет подписи
item.valueFontOptionsшрифт подписи, mutedшрифт/цвет текста значения
item.gapPixels18зазор между элементами в строке
item.rowGapPixels8зазор между строками
item.markerGapPixels6зазор между маркером и подписью
item.valueGapPixels14зазор между подписью и значением
item.hiddenOpacityFraction0.4прозрачность элемента скрытой серии
background.fillColorValueзаливка подложки
background.strokeColorValueцвет рамки подложки
background.strokeWidthPixels1толщина рамки
background.cornerRadiusPixels4скругление подложки
background.paddingPaddingValue8 / 0внутренние поля в CSS-стиле (ниже); 8 при заданных fill/stroke
background.shadowShadowOptionsтень под подложкой (ниже)
dataLegendItemOptions[]кастомные элементы (ниже)

Имя элемента — name серии (или yField, если имя не задано). showInLegend: false у серии убирает её элемент.

Непоместившиеся элементы пагинируются: внизу легенды появляются стрелки ‹ 1/3 › (горизонтальная легенда — maxRows строк на страницу, по умолчанию две). Для pie/donut клик по элементу скрывает сектор.

Ограничение размера

Легенда занимает столько, сколько нужно её элементам, и при длинных именах серий вертикальная легенда забирает это место у графика. maxWidth и maxHeight задают предел; их смысл следует из ориентации, которую задаёт position:

ЛегендаmaxWidthmaxHeight
вертикальная (left/right)не поместившиеся подписи обрезаются многоточиемэлементы сверх него уходят на следующую страницу
горизонтальная (top/bottom)элементы переносятся на следующую строку внутри негоограничивает строки на странице наряду с maxRows
js
legend: { position: 'right', maxWidth: 160 },

Оба принимают пиксели или строку с процентом — '40%' отсчитывается от места, которое разметка отвела легенде: чарт за вычетом отступов и заголовков, а для плавающей легенды — вся область чарта. Именно такой предел обычно и нужен резиновому чарту: доля остаётся прежней при любом размере.

js
legend: { position: 'right', maxWidth: '25%' },

Некорректное значение ('160px', '%') игнорируется — легенда остаётся без ограничения.

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

Плавающее размещение

position — это сторона докинга плюс необязательное выравнивание вдоль неё: top-right прижимает легенду к верхнему краю с выравниванием вправо (top центрирует). top-*/bottom-* раскладывают элементы горизонтальными рядами, left-*/right-* — вертикальной колонкой; ориентацию задаёт первое слово.

С floating: true легенда перестаёт резервировать место и рисуется поверх чарта (в духе CSS position: absolute). Якорь — вся область чарта, включая заголовки: заголовок с выравниванием влево и плавающая легенда top-right окажутся на одном уровне:

ts
import { getData } from './data';
import type { ChartOptions } from 'grafit-charts';

// A floating legend anchored to the top-right corner of the whole chart —
// on the same level as the left-aligned title.
export function createOptions(): ChartOptions {
  return {
    data: getData(),
    title: { text: 'Site traffic', textAlign: 'left', padding: { bottom: 4 } },
    subtitle: { text: 'visits per month, thousands', textAlign: 'left', padding: { bottom: 12 } },
    series: [
      { type: 'line', xField: 'month', yField: 'organic', name: 'Organic' },
      { type: 'line', xField: 'month', yField: 'ads', name: 'Ads' },
    ],
    legend: {
      position: 'top-right',
      floating: true,
      background: {
        fill: 'rgba(255, 255, 255, 0.9)',
        stroke: '#cbd5e1',
        cornerRadius: 6,
        // CSS-like shorthand: [vertical, horizontal]
        padding: [8, 12],
        // lifts the panel off the plot it overlays
        shadow: { color: 'rgba(15, 23, 42, 0.18)', blur: 10, offsetY: 3 },
      },
    },
  };
}
ts
export function getData() {
  return [
    { month: 'Jan', organic: 42, ads: 18 },
    { month: 'Feb', organic: 48, ads: 22 },
    { month: 'Mar', organic: 55, ads: 21 },
    { month: 'Apr', organic: 61, ads: 27 },
    { month: 'May', organic: 58, ads: 33 },
    { month: 'Jun', organic: 67, ads: 30 },
    { month: 'Jul', organic: 74, ads: 36 },
    { month: 'Aug', organic: 71, ads: 41 },
  ];
}

offset — отступ от закреплённых краёв (x от левого/правого, y от верхнего/нижнего); по центрированной оси положительное значение сдвигает вправо/вниз. background рисует подложку под элементами — полезно поверх графика.

Раз легенда ложится поверх зоны заголовков, title и subtitle по умолчанию её обтекают: строки на уровне бокса легенды переносятся внутри промежутка рядом с ним. avoidCaptions: false возвращает простое наложение — заголовки занимают всю ширину чарта, легенда рисуется поверх:

js
legend: { position: 'top-right', floating: true, avoidCaptions: false },

Подложка

background.padding принимает любую CSS-подобную запись: одно число, [вертикаль, горизонталь], [top, right, bottom, left] или { top, right, bottom, left } (те же формы работают у padding самого чарта).

background.shadow приподнимает подложку над тем, что она перекрывает. Любое поле включает тень; enabled: false убирает её.

ОпцияТипПо умолчаниюОписание
colorColorValuergba(0, 0, 0, 0.2)цвет тени
blurPixels8радиус размытия
offsetXPixels0сдвиг по горизонтали
offsetYPixels2сдвиг по вертикали
enabledbooleantruefalse убирает тень

Тень отбрасывает заливка подложки, поэтому нужен background.fill; рамка рисуется без тени.

Маркеры

item.marker задаёт фигуру для всех элементов; элемент data переопределяет её по полям.

ОпцияТипПо умолчаниюОписание
shapeLegendMarkerShape'square'circle, square, diamond, triangle, cross, plus, line
pathstringсвоя фигура как SVG path data; важнее shape
viewBoxnumber24сторона квадрата, в котором заданы координаты path
sizePixels10сторона бокса маркера (line рисуется в 1.8× шире)
strokeColorValueцвет обводки; без него фигура только с заливкой
strokeWidthPixels1толщина обводки и толщина маркера line
lineDashPixels[]штрихи для маркера line
cornerRadiusPixels3только square: скругление углов

line рисует штрих — так же, как серия выглядит на графике; path принимает обычную SVG-строку d, так что готовый набор иконок подключается напрямую. Координаты читаются в квадрате viewBox × viewBox и масштабируются до size, поэтому один и тот же d подходит любому размеру маркера:

ts
import { getData } from './data';
import type { ChartOptions } from 'grafit-charts';

// A five-pointed star in a 24×24 viewBox — the marker scales it to `size`.
const STAR = 'M12 2 L14.6 8.9 L21.8 9.3 L16.2 13.9 L18.1 21 L12 17 L5.9 21 L7.8 13.9 L2.2 9.3 L9.4 8.9 Z';

export function createOptions(): ChartOptions {
  return {
    data: getData(),
    title: { text: 'Delivery health' },
    subtitle: { text: 'per week' },
    series: [
      { type: 'bar', xField: 'week', yField: 'deploys', name: 'Deploys' },
      { type: 'line', xField: 'week', yField: 'failures', name: 'Failures', marker: { enabled: false } },
      { type: 'line', xField: 'week', yField: 'rollbacks', name: 'Rollbacks', lineDash: [5, 4] },
    ],
    legend: {
      item: { marker: { size: 12 }, gap: 22, markerGap: 8 },
      data: [
        // the bar keeps the default rounded square
        { name: 'Deploys', series: 'Deploys' },
        // a dash reads as a line the way the series draws it
        { name: 'Failures', series: 'Failures', marker: { shape: 'line', strokeWidth: 3 } },
        { name: 'Rollbacks', series: 'Rollbacks', marker: { shape: 'line', strokeWidth: 3, lineDash: [5, 4] } },
        // a static item with a custom glyph: SVG path data instead of a shape
        { name: 'SLO met', marker: { path: STAR, color: '#f59e0b' } },
      ],
    },
  };
}
ts
export function getData() {
  return [
    { week: 'W1', deploys: 24, failures: 5, rollbacks: 2 },
    { week: 'W2', deploys: 31, failures: 8, rollbacks: 3 },
    { week: 'W3', deploys: 19, failures: 3, rollbacks: 1 },
    { week: 'W4', deploys: 37, failures: 9, rollbacks: 4 },
    { week: 'W5', deploys: 28, failures: 4, rollbacks: 1 },
    { week: 'W6', deploys: 34, failures: 6, rollbacks: 2 },
  ];
}

Кастомные элементы

legend.data полностью заменяет автоматические элементы из серий. Пригодится, когда цвета несут смысл внутри одной серии — например, гант из range-bar с заливкой через колбэк по статусу:

ts
import { getData } from './data';
import type { ChartOptions, LegendItemOptions } from 'grafit-charts';

// Custom legend items describe the bar statuses of a Gantt-style range-bar —
// something the auto-derived per-series legend cannot show.
const STATUS_COLORS: Record<string, string> = {
  done: '#22c55e',
  running: '#3b82f6',
  failed: '#ef4444',
  queued: '#94a3b8',
};

export function createOptions(): ChartOptions {
  const data = getData();
  const countOf = (status: string) => String(data.filter((datum) => datum.status === status).length);
  const legendItem = (status: string, name: string): LegendItemOptions => ({
    name,
    marker: { color: STATUS_COLORS[status] },
    value: countOf(status),
  });
  return {
    data,
    title: { text: 'Pipeline run' },
    subtitle: { text: 'task timeline, hours' },
    series: [
      {
        type: 'range-bar',
        xField: 'task',
        yLowField: 'start',
        yHighField: 'end',
        direction: 'horizontal',
        cornerRadius: 3,
        fill: ({ datum }) => STATUS_COLORS[String(datum.status)] ?? '#94a3b8',
      },
    ],
    legend: {
      data: [
        legendItem('done', 'Done'),
        legendItem('running', 'Running'),
        { ...legendItem('failed', 'Failed'), marker: { color: STATUS_COLORS.failed, size: 12 }, label: { fontWeight: 'bold', color: '#ef4444' } },
        legendItem('queued', 'Queued'),
      ],
    },
  };
}
ts
export function getData() {
  return [
    { task: 'extract', start: 0, end: 3, status: 'done' },
    { task: 'validate', start: 3, end: 5, status: 'done' },
    { task: 'transform', start: 5, end: 11, status: 'running' },
    { task: 'notify', start: 8, end: 10, status: 'failed' },
    { task: 'load', start: 11, end: 14, status: 'queued' },
    { task: 'report', start: 14, end: 16, status: 'queued' },
  ];
}
ОпцияТипОписание
namestringтекст элемента (обязательно)
seriesstringпривязка к серии для переключения
marker.colorColorValueцвет маркера; привязанный элемент наследует цвет серии
markerLegendMarkerOptionsфигура/path/размер — все опции маркера поверх item.marker
labelFontOptionsшрифт/цвет подписи элемента
valuestringзначение справа от подписи

series сопоставляется сначала с id серии, затем с её name. Привязанный элемент переключает серию по клику и тускнеет, когда она скрыта; элемент без series (или с неизвестной ссылкой) статичный — рисуется, но клик ничего не делает. Для pie/donut привязка к отдельному сектору — по его метке (или явному id#index); привязка к pie-серии целиком не поддерживается.