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

Кастомные подсказки в гео-компонентах Nivo формируют один из ключевых механизмов взаимодействия пользователя с картографической визуализацией. Географические графики редко несут смысл без контекстных данных, и именно tooltip становится основным способом раскрытия дополнительной информации о регионе, точке или полигоне.

В библиотеке Nivo (в частности в @nivo/geo) подсказки реализуются через параметр tooltip, который позволяет полностью переопределить стандартное поведение всплывающего окна. Важно учитывать, что гео-компоненты оперируют GeoJSON-структурами, где каждый feature содержит набор свойств (properties), используемых для отображения и взаимодействия.

В географических визуализациях Nivo tooltip вызывается при наведении на:

  • страны или регионы (choropleth map)
  • пользовательские полигоны
  • маркеры (points)
  • интерактивные слои (layers)

Стандартная сигнатура tooltip-функции:

(feature) => ReactNode

Чаще всего передается объект с полями:

{
  feature: GeoJSONFeature,
  value: number,
  color: string
}

или в зависимости от типа карты:

{
  point: {
    latitude: number,
    longitude: number,
    data: object
  }
}

Кастомизация tooltip для Choropleth карты

Choropleth карта из @nivo/geo визуализирует числовые значения по регионам. Каждый регион (feature) содержит свойства, определяемые GeoJSON.

Базовая настройка tooltip:

import { ResponsiveChoropleth } from '@nivo/geo'

const GeoTooltip = ({ feature }) => {
    return (
        <div
            style={{
                background: 'white',
                padding: '10px 12px',
                border: '1px solid #ccc',
                borderRadius: 6
            }}
        >
            <strong>{feature.properties.name}</strong>
        </div>
    )
}

const MyMap = () => (
    <ResponsiveChoropleth
        data={[]}
        features={[]}
        tooltip={GeoTooltip}
        domain={[0, 100]}
        unknownColor="#999"
    />
)

Ключевым моментом является доступ к feature.properties, где обычно хранится название региона, код страны и дополнительные атрибуты.

Работа с value и цветовой моделью

В большинстве сценариев choropleth данные связываются через ключ id:

const data = [
  { id: 'RUS', value: 120 },
  { id: 'USA', value: 300 }
]

Tooltip может быть расширен для отображения числовых значений:

const GeoTooltip = ({ feature, value }) => {
    return (
        <div style={{ padding: 10, background: '#fff' }}>
            <div>{feature.properties.name}</div>
            <div>Значение: {value ?? 'нет данных'}</div>
        </div>
    )
}

При отсутствии значения используется unknownColor, однако tooltip при этом продолжает получать feature без value.

Кастомные подсказки для маркеров (GeoPoints)

В @nivo/geo часто используется слой маркеров для отображения городов, точек интереса или событий.

Пример конфигурации:

const markers = [
  {
    name: 'Almaty',
    coordinates: [76.8897, 43.2389],
    value: 42
  }
]

Tooltip для маркеров получает объект point:

const PointTooltip = ({ point }) => {
    const { data } = point

    return (
        <div style={{ background: 'white', padding: 8 }}>
            <div>{data.name}</div>
            <div>Координаты: {data.coordinates.join(', ')}</div>
            <div>Значение: {data.value}</div>
        </div>
    )
}

Использование:

<ResponsiveChoropleth
    features={features}
    data={data}
    markers={markers}
    tooltip={PointTooltip}
/>

Уровни кастомизации tooltip

В гео-визуализациях Nivo tooltip можно строить на нескольких уровнях сложности:

1. Простое текстовое представление

Минимальная форма:

tooltip={({ feature }) => feature.properties.name}

Подходит для случаев, когда визуальный слой уже несет всю информацию.

2. Стилизация с контекстом данных

Добавление цвета и числовых значений:

tooltip={({ feature, value, color }) => (
    <div style={{ borderLeft: `4px solid ${color}`, padding: 8 }}>
        <div>{feature.properties.name}</div>
        <div>{value}</div>
    </div>
)}

3. Компонентный tooltip

Полноценный React-компонент:

const DetailedTooltip = ({ feature, value }) => {
    const region = feature.properties

    return (
        <div className="tooltip">
            <div className="tooltip-title">{region.name}</div>
            <div className="tooltip-row">
                <span>ID:</span> {region.id}
            </div>
            <div className="tooltip-row">
                <span>Value:</span> {value}
            </div>
        </div>
    )
}

Работа с GeoJSON и нестандартными properties

GeoJSON в гео-компонентах может содержать произвольные свойства:

{
  "type": "Feature",
  "properties": {
    "name": "Kazakhstan",
    "population": 19000000,
    "gdp": 220000000000
  }
}

Tooltip может использовать эти поля напрямую:

const Tooltip = ({ feature }) => {
    const p = feature.properties

    return (
        <div>
            <div>{p.name}</div>
            <div>Население: {p.population}</div>
            <div>ВВП: {p.gdp}</div>
        </div>
    )
}

Такой подход позволяет переносить бизнес-логику прямо в слой визуализации.

Управление поведением tooltip

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

  • позиционирование (автоматическое следование курсору)
  • задержки отображения
  • кастомные контейнеры
  • отключение стандартного контейнера

Пример отключения встроенного оформления:

<ResponsiveChoropleth
    tooltip={Tooltip}
    theme={{
        tooltip: {
            container: {
                background: 'transparent',
                boxShadow: 'none'
            }
        }
    }}
/>

Оптимизация рендеринга tooltip

При больших GeoJSON наборах важно учитывать производительность:

  • избегается создание новых функций внутри render без необходимости
  • memoization tooltip-компонентов
  • минимизация тяжелых вычислений внутри tooltip

Оптимизированный вариант:

import { memo } from 'react'

const Tooltip = memo(({ feature }) => {
    const name = feature.properties.name

    return <div>{name}</div>
})

Интеграция с кастомными слоями

Geo-компоненты Nivo поддерживают layers, и tooltip часто зависит от слоя.

Пример добавления кастомного слоя:

const CustomLayer = ({ features }) => {
    return null
}

<ResponsiveChoropleth
    layers={[
        'features',
        'borders',
        CustomLayer
    ]}
    tooltip={Tooltip}
/>

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

Типизация tooltip в TypeScript

При использовании TypeScript структура tooltip становится более строгой:

type GeoFeature = {
    feature: GeoJSON.Feature
    value?: number
    color?: string
}

const Tooltip = ({ feature, value }: GeoFeature) => {
    return (
        <div>
            {feature.properties?.name}
            {value}
        </div>
    )
}

Жесткая типизация снижает риск обращения к несуществующим полям GeoJSON.

Расширенные сценарии отображения

В сложных приложениях tooltip в гео-картах выполняет функции мини-панели аналитики:

  • динамическое сравнение регионов
  • отображение трендов
  • вложенные показатели (GDP, population, density)
  • условная стилизация

Пример условного форматирования:

const Tooltip = ({ feature, value }) => {
    const level =
        value > 200 ? 'high' :
        value > 100 ? 'medium' : 'low'

    return (
        <div>
            <div>{feature.properties.name}</div>
            <div>Уровень: {level}</div>
        </div>
    )
}

Связь tooltip с интерактивностью карты

Tooltip в гео-компонентах тесно связан с событиями:

  • onMouseEnter
  • onMouseMove
  • onMouseLeave

Хотя Nivo абстрагирует эти события, кастомный tooltip фактически работает поверх них, используя внутренний event layer.

В более сложных сценариях tooltip может синхронизироваться с внешними компонентами:

  • таблицами
  • фильтрами
  • графиками
  • таймлайнами

Это позволяет превращать гео-карту в центральный элемент аналитического интерфейса.