В круговых и кольцевых диаграммах Nivo (PieChart, DonutChart) подсказки работают не с отдельной точкой данных, а с группой элементов, объединённых в «срез» (slice). Именно для этого используется концепция SliceTooltip — единый компонент, который отображает агрегированную информацию по активному сектору диаграммы.
SliceTooltip активируется при наведении на сектор и получает контекст сразу по всем элементам, входящим в этот срез. В отличие от точечных графиков, где tooltip связан с одной точкой, здесь модель взаимодействия строится вокруг сегмента.
Основной смысл SliceTooltip заключается в том, чтобы:
При наведении на сектор диаграммы Nivo формирует объект
slice, содержащий набор points, относящихся к
активному сегменту. Каждый point включает:
SliceTooltip получает этот объект и рендерит его в пользовательском компоненте.
Типовая структура данных:
{
id: "react",
value: 120,
color: "#61dafb",
label: "React"
}
В случае составных диаграмм slice может содержать несколько элементов, особенно при stacked-структурах или группировке.
В Nivo кастомизация tooltip осуществляется через свойство
tooltip. Для pie-chart оно применяется к срезу:
import { ResponsivePie } from '@nivo/pie'
const MySliceTooltip = ({ slice }) => {
return (
<div style={{ padding: 12, background: 'white', border: '1px solid #ccc' }}>
<strong>{slice.id}</strong>
<div>Значение: {slice.value}</div>
</div>
)
}
const data = [
{ id: 'React', value: 120 },
{ id: 'Vue', value: 80 },
{ id: 'Angular', value: 60 }
]
export default function Chart() {
return (
<ResponsivePie
data={data}
tooltip={MySliceTooltip}
/>
)
}
В данном случае tooltip получает объект slice и полностью контролирует отображение информации.
Одной из ключевых задач является преобразование сырых значений в читаемый формат. Часто требуется:
Пример вычисления процента:
const MySliceTooltip = ({ slice }) => {
const total = slice.data.reduce((acc, item) => acc + item.value, 0)
const percent = ((slice.value / total) * 100).toFixed(2)
return (
<div style={{ padding: 10 }}>
<div>{slice.id}</div>
<div>{slice.value} ({percent}%)</div>
</div>
)
}
В реальных сценариях часто применяется предрасчёт total на уровне данных, чтобы избежать вычислений при каждом hover-событии.
Nivo не ограничивает структуру tooltip, позволяя создавать полностью кастомные UI-блоки. Это важно для интеграции с дизайн-системами.
Пример стилизации:
const MySliceTooltip = ({ slice }) => {
return (
<div className="tooltip">
<div className="tooltip__title">{slice.id}</div>
<div className="tooltip__row">
<span>Значение:</span>
<span>{slice.value}</span>
</div>
</div>
)
}
CSS:
.tooltip {
background: #1e1e1e;
color: white;
padding: 12px;
border-radius: 6px;
font-size: 12px;
}
.tooltip__title {
font-weight: 600;
margin-bottom: 6px;
}
Кастомизация позволяет выстраивать визуальную согласованность с интерфейсом приложения, особенно в аналитических панелях.
SliceTooltip тесно связан с состоянием active slice. При наведении происходит:
Дополнительно можно управлять поведением через:
activeOuterRadiusOffset — увеличение активного
сектора;arcLinkLabels — подписи;onMouseEnter / onMouseLeave — кастомные
обработчики.Пример:
<ResponsivePie
data={data}
activeOuterRadiusOffset={8}
tooltip={MySliceTooltip}
onMouseEn ter={(data) => console.log('hover', data.id)}
/>
В линейных, точечных и комбинированных графиках Nivo используется другая модель — point tooltip, где каждая подсказка привязана к конкретной точке данных.
В отличие от SliceTooltip:
Point tooltip получает объект точки:
{
x: "Jan",
y: 42,
serieId: "sales",
color: "#ff9900"
}
Простейшая реализация:
const MyPointTooltip = ({ point }) => {
return (
<div style={{ padding: 10, background: 'white' }}>
<div>Серия: {point.serieId}</div>
<div>X: {point.data.x}</div>
<div>Y: {point.data.y}</div>
</div>
)
}
import { ResponsiveLine } from '@nivo/line'
const data = [
{
id: 'sales',
data: [
{ x: 'Jan', y: 30 },
{ x: 'Feb', y: 45 },
{ x: 'Mar', y: 60 }
]
}
]
export default function Chart() {
return (
<ResponsiveLine
data={data}
tooltip={MyPointTooltip}
/>
)
}
Каждая точка графика становится активной зоной, при наведении на которую создаётся tooltip.
В point tooltip часто требуется унификация формата данных:
Пример:
const MyPointTooltip = ({ point }) => {
const formatValue = (v) => `${v} ₽`
return (
<div>
<div>{point.data.x}</div>
<div>{formatValue(point.data.y)}</div>
</div>
)
}
Для временных рядов часто применяется преобразование дат:
const date = new Date(point.data.xFormatted)
В графиках с несколькими сериями point tooltip может отображать:
Nivo поддерживает режим crosshair, при котором tooltip синхронизируется между всеми сериями по одной оси X.
Пример:
<ResponsiveLine
data={data}
useMesh={true}
tooltip={MyPointTooltip}
/>
useMesh усиливает точность попадания в точки и улучшает
UX при плотных данных.
SliceTooltip и point tooltip различаются по логике взаимодействия:
В сложных дашбордах применяется единый подход к визуализации подсказок. Иногда SliceTooltip и point tooltip приводятся к общей модели:
Пример унифицированного форматтера:
const formatValue = (value, type) => {
switch (type) {
case 'currency':
return `${value} ₽`
case 'percent':
return `${value}%`
default:
return value
}
}
Tooltip в Nivo рендерится часто, особенно при движении мыши. Для оптимизации используются:
React.memo);Пример мемоизации:
const MySliceTooltip = React.memo(({ slice }) => {
return (
<div>
{slice.id}: {slice.value}
</div>
)
})
При неполных данных tooltip может получать:
В таких случаях требуется защитная логика:
if (!slice) return null
if (slice.value == null) return null
Это предотвращает некорректный рендеринг всплывающих окон.
Nivo автоматически управляет позиционированием tooltip, однако кастомные компоненты могут влиять на:
Часто используется фиксированная минимальная ширина:
.tooltip {
min-width: 120px;
pointer-events: none;
}
pointer-events: none предотвращает «дрожание» при
наведении.