Кастомные подсказки для Pie

Визуализация круговых диаграмм в @nivo/pie предоставляет гибкую систему настройки подсказок (tooltips), позволяющую управлять тем, как отображается информация при наведении на сегменты. Кастомизация tooltip — один из ключевых инструментов для повышения читаемости и информативности диаграмм, особенно в аналитических интерфейсах, где требуется точное представление долей, процентов и метаданных.


Базовая модель tooltip в Pie

В @nivo/pie каждый элемент диаграммы (slice) передает в tooltip следующие данные:

  • id — идентификатор сегмента
  • value — числовое значение
  • formattedValue — форматированное значение (если задан форматтер)
  • color — цвет сегмента
  • datum — исходный объект данных

Tooltip вызывается автоматически при наведении на сектор и получает объект с этими данными.


Стандартное поведение tooltip

По умолчанию используется встроенный tooltip-компонент, который отображает:

  • название сегмента
  • значение
  • процент (если включена нормализация)

Пример базовой конфигурации:

import { ResponsivePie } from '@nivo/pie';

const data = [
  { id: 'JavaScript', value: 40 },
  { id: 'Python', value: 25 },
  { id: 'Go', value: 15 },
  { id: 'C++', value: 20 }
];

export default function PieChart() {
  return (
    <ResponsivePie
      data={data}
      margin={{ top: 40, right: 80, bottom: 80, left: 80 }}
      innerRadius={0.5}
      padAngle={1}
      cornerRadius={3}
    />
  );
}

Подключение кастомного tooltip

Кастомизация осуществляется через проп tooltip, который принимает React-компонент или функцию.

Базовая форма:

tooltip: (props) => ReactNode

props содержит все данные текущего сегмента.


Простой кастомный tooltip

Наиболее распространённый вариант — замена стандартного вывода на более читаемый блок с акцентом на цвет и процент.

const CustomTooltip = ({ datum }) => {
  return (
    <div
      style={{
        padding: '10px 12px',
        background: '#1e1e1e',
        color: '#fff',
        borderRadius: 6,
        boxShadow: '0 2px 10px rgba(0,0,0,0.25)'
      }}
    >
      <strong>{datum.id}</strong>
      <div>Значение: {datum.value}</div>
    </div>
  );
};

Подключение:

<ResponsivePie
  data={data}
  innerRadius={0.6}
  tooltip={CustomTooltip}
/>

Использование форматирования процентов

Часто требуется отображать долю в процентах относительно суммы всех значений. Для этого вычисление выполняется внутри tooltip.

const CustomTooltip = ({ datum, value, allData }) => {
  const total = allData.reduce((acc, item) => acc + item.value, 0);
  const percent = ((datum.value / total) * 100).toFixed(1);

  return (
    <div style={{ padding: 10, background: '#222', color: '#fff' }}>
      <div>{datum.id}</div>
      <div>{datum.value} ({percent}%)</div>
    </div>
  );
};

В @nivo/pie доступ к полному набору данных может быть организован через внешний scope или через кастомную обёртку над компонентом.


Tooltip с цветовой индикацией

Цвет сегмента можно использовать для визуального связывания tooltip с диаграммой.

const CustomTooltip = ({ datum }) => {
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 8, padding: 10 }}>
      <span
        style={{
          width: 12,
          height: 12,
          backgroundColor: datum.color,
          display: 'inline-block'
        }}
      />
      <div>
        <div>{datum.id}</div>
        <div>{datum.value}</div>
      </div>
    </div>
  );
};

Такой подход улучшает визуальную связность между подсказкой и сегментом диаграммы.


Условное отображение tooltip

Tooltip может быть адаптивным и менять структуру в зависимости от значения или категории данных.

const CustomTooltip = ({ datum }) => {
  const isLarge = datum.value > 30;

  return (
    <div
      style={{
        padding: 10,
        background: isLarge ? '#2e7d32' : '#424242',
        color: '#fff'
      }}
    >
      <div>{datum.id}</div>
      <div>
        {isLarge ? 'Высокое значение' : 'Обычное значение'}: {datum.value}
      </div>
    </div>
  );
};

Tooltip с дополнительными метаданными

Если исходные данные содержат дополнительные поля, они могут быть использованы внутри tooltip без ограничений.

const data = [
  {
    id: 'React',
    value: 45,
    description: 'Frontend библиотека',
    projects: 120
  }
];

const CustomTooltip = ({ datum }) => {
  return (
    <div style={{ padding: 12, background: '#111', color: '#fff' }}>
      <div>{datum.id}</div>
      <div>{datum.data.description}</div>
      <div>Проектов: {datum.data.projects}</div>
    </div>
  );
};

Анимация tooltip

Хотя @nivo/pie не управляет анимацией tooltip напрямую, можно использовать CSS для создания плавного появления.

.tooltip {
  opacity: 0;
  transform: translateY(5px);
  animation: fadeIn 0.15s ease forwards;
}

@keyframes fadeIn {
  to {
    opacity: 1;
    transform: translateY(0);
  }
}
const CustomTooltip = ({ datum }) => {
  return (
    <div className="tooltip" style={{ padding: 10, background: '#000', color: '#fff' }}>
      {datum.id}: {datum.value}
    </div>
  );
};

Использование tooltip вне стандартного hover

В некоторых интерфейсах требуется фиксированное отображение подсказки (например, для аналитических панелей). В этом случае tooltip можно комбинировать с состоянием React:

const [active, setActive] = useState(null);

<ResponsivePie
  data={data}
  tooltip={({ datum }) => {
    setActive(datum);
    return null;
  }}
/>

{active && (
  <div className="fixed-tooltip">
    {active.id}: {active.value}
  </div>
)}

Такой подход переносит контроль над отображением полностью в пользовательский слой.


Интеграция tooltip с внешними UI-компонентами

Tooltip может использоваться не только как всплывающий элемент, но и как источник данных для внешних панелей.

const [info, setInfo] = useState(null);

<ResponsivePie
  data={data}
  tooltip={({ datum }) => {
    setInfo(datum);
    return <div />;
  }}
/>

<Sidebar>
  {info ? (
    <>
      <h3>{info.id}</h3>
      <p>{info.value}</p>
    </>
  ) : (
    <p>Наведи на сектор</p>
  )}
</Sidebar>

Управление стилями через тему tooltip

При использовании дизайн-системы tooltip обычно подстраивается под глобальную тему:

const theme = {
  tooltip: {
    container: {
      background: '#0f172a',
      color: '#e2e8f0',
      fontSize: 12
    }
  }
};

<ResponsivePie data={data} theme={theme} />

Оптимизация tooltip при больших наборах данных

При работе с диаграммами, содержащими десятки и сотни сегментов, важно минимизировать нагрузку:

  • избегать сложных вычислений внутри tooltip
  • кешировать суммарные значения
  • не создавать новые функции на каждый рендер
  • использовать мемоизацию React.memo

Обработка ошибок и пустых значений

Tooltip должен учитывать возможные некорректные данные:

const CustomTooltip = ({ datum }) => {
  if (!datum || typeof datum.value !== 'number') return null;

  return (
    <div>
      <div>{datum.id ?? 'unknown'}</div>
      <div>{datum.value ?? 0}</div>
    </div>
  );
};

Поведение tooltip при перекрытиях и плотных сегментах

В случае маленьких сегментов диаграммы tooltip может срабатывать нестабильно. В таких случаях применяется:

  • увеличение padAngle
  • использование activeOuterRadiusOffset
  • упрощение структуры tooltip
<ResponsivePie
  data={data}
  padAngle={2}
  activeOuterRadiusOffset={8}
/>