import React from 'react'; import type { ActiveTarget, HitQuery } from '../core/hittest/types'; import type { ChartRegistration } from '../core/hittest/registration'; /** * Pair of numeric domains for x and y axes */ export interface DomainPair { /** X-axis domain range */ x: [number, number]; /** Y-axis domain range */ y: [number, number]; } /** * Configuration options for chart interactions */ export interface InteractionConfig { /** Enable panning and zooming interactions. Default true. */ enablePanZoom?: boolean; /** Zoom mode: 'x' (default), 'y', or 'both'. */ zoomMode?: 'x' | 'y' | 'both'; /** Minimum zoom level (relative to initial domain width/height). Default 0.1 (10%). */ minZoom?: number; /** Step factor for each wheel event (smaller = finer). Default 0.1 */ wheelZoomStep?: number; /** Reset zoom to initial domains on double tap. Default true. */ resetOnDoubleTap?: boolean; /** Enable zooming via mouse wheel. Default false. */ enableWheelZoom?: boolean; /** Minimum pixel delta before wheel zooming occurs (noise filter). Default 0 (disabled). */ wheelZoomPixelThreshold?: number; /** Minimum zoom level when using wheel zoom (relative to initial domain width/height). Default 0.05 (5%). */ wheelMinZoom?: number; /** Clamp panning to initial domains (no blank space). Default false. */ clampToInitialDomain?: boolean; /** Show a vertical guide line tracking the active point. Default false. */ enableCrosshair?: boolean; /** Show a tooltip for the nearest point(s) to the pointer. Default false. */ liveTooltip?: boolean; /** Show a tooltip for all series at the pointer x (vertical slice). Default false. */ multiTooltip?: boolean; /** Invert the direction of pinch zooming. Default false. */ invertPinchZoom?: boolean; /** Invert the direction of wheel zooming. Default false. */ invertWheelZoom?: boolean; /** Render the shared tooltip in a portal attached to document.body (web only) using page coordinates. Default true. */ popoverPortal?: boolean; /** Throttle high-frequency pointer updates to animation frames (reduces rerenders). Default true. */ pointerRAF?: boolean; /** Minimum pixel delta before pointer state update (noise filter). Default 0 (disabled). */ pointerPixelThreshold?: number; /** Max rows shown in a multi-series slice tooltip. Default 8. */ aggregatorMaxSeries?: number; } /** * Individual point within a registered series */ export interface RegisteredSeriesPoint { /** X coordinate in data space */ x: number; /** Y coordinate in data space */ y: number; /** Optional metadata associated with the point */ meta?: any; /** Optional pixel X coordinate (relative to the chart container) */ pixelX?: number; /** Optional pixel Y coordinate (relative to the chart container) */ pixelY?: number; } /** * Data series registered with the interaction provider */ export interface RegisteredSeries { /** Unique identifier for the series */ id: string | number; /** Display name for the series */ name?: string; /** Color used to render the series */ color?: string; /** Data points belonging to the series */ points: RegisteredSeriesPoint[]; /** Whether the series is currently visible */ visible: boolean; } /** * High-frequency ("volatile") state — changes on every pointer move. Lives in its own * context so only the components that actually render pointer-driven visuals (the tooltip * + the crosshair charts) re-render each frame. Chart bodies read the stable context and * never see these, so a hover sweep doesn't re-render the whole chart. */ export interface ChartVolatileState { /** Current pointer/mouse position */ pointer: { x: number; y: number; inside: boolean; insideX?: boolean; insideY?: boolean; pageX?: number; pageY?: number; data?: any; } | null; /** * Normalized active target from the hit-test engine. Carries geometry-specific * fields (categoryIndex / cell / angleDeg / axisIndex) and a canonical pixel anchor. */ activeTarget: ActiveTarget | null; /** * All series' targets at the current pointer x/angle (a "slice"), for * multi-series tooltips. Empty unless a tester with slice() is active and * multiTooltip is on. */ activeSlice: ActiveTarget[]; } /** Low-frequency ("stable") state — changes rarely (legend toggle, zoom, layout). */ interface StableState { /** * Series registry — now used purely for legend visibility (upserted by * `updateSeriesVisibility`). `points` is vestigial (always `[]`); the legacy * tooltip that consumed it was retired. */ series: RegisteredSeries[]; /** Initial and current domains */ domains: { initial: DomainPair; current: DomainPair; } | null; /** Offset of the chart root element */ rootOffset?: { left: number; top: number; } | null; } /** * Stable context value provided to chart bodies — everything EXCEPT the per-frame * volatile fields (pointer/activeTarget/activeSlice), which live in the volatile context. * Setters live here so charts can feed the store without subscribing to volatile state. */ interface InteractionContextValue extends StableState { /** Interaction configuration */ config: InteractionConfig; /** Update visibility of a series (upserts a visibility-only entry for legend toggles). */ updateSeriesVisibility: (id: string | number, visible: boolean) => void; /** Update pointer position */ setPointer: (p: ChartVolatileState['pointer']) => void; /** Update the normalized active target (new hit-test engine). */ setActiveTarget: (t: ActiveTarget | null) => void; /** Update the multi-series slice (new hit-test engine). */ setActiveSlice: (s: ActiveTarget[]) => void; /** * Register (or replace) a chart's hit-test geometry under a stable key. The * store derives a HitTester from the registration. Call with `null` to * unregister on unmount. */ register: (key: string | number, reg: ChartRegistration | null) => void; /** Run the registered hit-testers and return the closest target (best distance). */ hitTest: (q: HitQuery) => ActiveTarget | null; /** Update domains (flexible signature) */ setDomains: (d: DomainPair['x'] | DomainPair['y'] | Partial) => void; /** Initialize domains with initial values */ initializeDomains: (initial: DomainPair) => void; /** Reset zoom to initial domains */ resetZoom: () => void; /** Set the root element offset */ setRootOffset: (o: { left: number; top: number; }) => void; } /** Pointer position — changes on every pointer move (per-frame during hover). */ export type PointerState = ChartVolatileState['pointer']; /** Resolved hit-test target(s) — changes only when the active mark changes (deduped). */ export interface TargetState { activeTarget: ActiveTarget | null; activeSlice: ActiveTarget[]; } /** * Hook to access the (stable) chart interaction context — config, series, setters, * register/hitTest, domains. Does NOT subscribe to per-frame pointer/target/slice, so a * component reading this does not re-render on pointer moves. * @throws Error if used outside of ChartInteractionProvider */ export declare const useChartInteractionContext: () => InteractionContextValue; /** * Non-throwing variant. Returns null when there is no provider, replacing the * copy-pasted `try { useChartInteractionContext() } catch {}` blocks. */ export declare const useOptionalChartInteraction: () => InteractionContextValue | null; /** * Subscribe to the raw pointer position ONLY. Re-renders every frame during hover, so use * it only for cursor-following visuals (crosshair line, hover readout). Returns null when * there is no provider. */ export declare const usePointer: () => PointerState; /** * Subscribe to the resolved hit-test target/slice ONLY. Re-renders when the active mark * changes, NOT on every pointer move — the right hook for "highlight the active mark". * Returns EMPTY_TARGET when there is no provider. */ export declare const useActiveTarget: () => TargetState; /** * Combined convenience: pointer + target. Re-renders every frame (it reads the pointer), * so prefer usePointer()/useActiveTarget() when a component needs only one. Returns * EMPTY_VOLATILE when there is no provider. */ export declare const useChartInteractionVolatile: () => ChartVolatileState; /** * Provider component for chart interaction state and behaviors */ export declare const ChartInteractionProvider: React.FC<{ config?: InteractionConfig; children: React.ReactNode; }>; export {};