import { type SkFont } from "@shopify/react-native-skia"; import { type Gesture } from "react-native-gesture-handler"; import type { DerivedValue, SharedValue } from "react-native-reanimated"; import { type ChartPadding } from "../draw/line"; import type { CandleGap, CandlePoint } from "../types"; /** Horizontal travel required before a plain scrub claims the touch. */ export declare const SCRUB_ACTIVATE_X_PX = 20; /** Vertical travel that fails a plain scrub so a parent scroll gesture can win. */ export declare const SCRUB_FAIL_Y_PX = 10; /** Clamp an X pixel to the plot's horizontal bounds. */ export declare function clampPlotX(x: number, padLeft: number, canvasWidth: number, padRight: number): number; export interface ScrubHitSlop { left?: number; right?: number; bottom?: number; } /** Build touch-down bounds for a scrub recognizer. */ export declare function resolveScrubHitSlop(padding: ChartPadding, clampToPlot: boolean, bottomExclude?: number): ScrubHitSlop | undefined; interface ScrubGestureValue { get(): T; set(value: T): void; } /** Start plain scrubbing; the recognizer gates out-of-plot touch origins. */ export declare function startPlainScrub(x: number, padding: ChartPadding, canvasWidth: number, clampToPlot: boolean, scrubX: ScrubGestureValue, scrubActive: ScrubGestureValue, gestureStarted: ScrubGestureValue): void; /** Track plain scrubbing, clamping gestures accepted by the recognizer. */ export declare function updatePlainScrub(x: number, padding: ChartPadding, canvasWidth: number, clampToPlot: boolean, scrubX: ScrubGestureValue, scrubActive: ScrubGestureValue): void; export interface TooltipLayout { x: number; y: number; w: number; h: number; valueStr: string; timeStr: string; valueTextX: number; timeTextX: number; line1Y: number; line2Y: number; /** Multi-series tooltip: time + per-series rows (replaces valueStr/timeStr rendering). */ stackedLines?: { text: string; textX: number; baselineY: number; dim: boolean; }[]; /** LiveChartSeries' opt-in Morfi-style time pill + per-series value pills. */ perSeries?: { /** Idle endpoint mode: value pills only, with no guide or time pill. */ pinned: boolean; timePill?: { x: number; y: number; w: number; h: number; text: string; textX: number; baselineY: number; }; pills: { id: string; color: string; anchorX: number; anchorY: number; x: number; y: number; w: number; h: number; dotX: number; dotY: number; label: string; labelX: number; value: string; valueX: number; baselineY: number; }[]; }; } export declare const HIDDEN_TOOLTIP: TooltipLayout; export interface CrosshairState { scrubX: SharedValue; scrubActive: SharedValue; scrubTime: SharedValue; scrubValue: SharedValue; crosshairOpacity: SharedValue; tooltipLayout: SharedValue | DerivedValue; /** Scrub intersection Y in canvas px; -1 when there's no dot to draw * (inactive / no value / degenerate range). See {@link computeScrubDotY}. */ scrubDotY: SharedValue; /** OHLC candle under the crosshair in candle mode (`null` in line mode or when * inactive) — single-series `useCrosshair` only; undefined on the multi-series * crosshair. Surfaced for a custom candle `renderTooltip`. */ scrubCandle?: SharedValue; /** Explicit line or candle gap under the crosshair; single-series only. */ scrubGap?: SharedValue; /** Canvas-Y where the crosshair line should start so it stops at a top-pinned * custom tooltip's bottom edge instead of running up through it. Written by * {@link CustomTooltipOverlay} (the label's measured bottom) when * `tooltipPlacement: "top"` is active, read by {@link CrosshairOverlay}; -1 * means "no top tooltip" → the line starts at `padding.top` as before. * Single-series `useCrosshair` only; undefined on the multi-series crosshair. */ tooltipLineTop?: SharedValue; gesture: ReturnType; /** Whether a reticle is currently placed/locked. */ lockActive?: SharedValue; /** Locked reticle X in canvas px (-1 when none). */ lockX?: SharedValue; /** Locked reticle Y in canvas px (-1 when none). */ lockY?: SharedValue; /** Chosen price = value at the reticle Y (optionally snapped); null when none. */ lockPrice?: SharedValue; /** Right-gutter action-badge layout (also the tap hit rect). */ actionBadge?: SharedValue; /** X-axis time-badge layout at the reticle (opt-in; hidden when off). */ timeBadge?: SharedValue; /** Tap gesture that places/moves the reticle, presses the badge, or dismisses. */ tapGesture?: ReturnType; } /** * Maps a scrub value to its Y pixel at the crosshair intersection — the same * mapping the live dot and tooltip use. Returns -1 when there is no dot to draw * (value is null or the canvas isn't laid out); a degenerate (zero) range pins * the dot to the vertical center of the plot. */ export declare function computeScrubDotY(value: number | null, displayMin: number, displayMax: number, canvasHeight: number, padTop: number, padBottom: number): number; /** * Inverse of {@link computeScrubDotY}: maps a canvas Y pixel back to a value (a * free price *level*, used by scrub-action mode where the reticle Y is the chosen * price, not the line value at the reticle X). Returns null when the canvas isn't * laid out. A degenerate (zero) range collapses to the single value; a Y dragged * past the plot edges clamps to `displayMin` / `displayMax` rather than * extrapolating to a nonsensical price. */ export declare function computeValueAtY(y: number, displayMin: number, displayMax: number, canvasHeight: number, padTop: number, padBottom: number): number | null; /** Rounds `price` to the nearest `increment` (no-op when increment is falsy/≤0). */ export declare function snapPrice(price: number, increment?: number): number; /** * Maps a scrub X position to a window timestamp. * Returns -1 when inactive or when the canvas is not yet laid out. */ export declare function computeScrubTime(scrubActive: boolean, scrubX: number, padding: ChartPadding, canvasWidth: number, timestamp: number, windowSecs: number): number; /** * Quantizes a scrub X to the center of the candle whose time bucket contains * it (`scrub.snapToCandles`) — the inverse of {@link computeScrubTime} * followed by the forward mapping of the picked candle's center. Returns the * raw X unchanged when the position falls in a gap between candles or when * the plot has no horizontal extent / time window yet. */ export declare function snapScrubXToCandleCenter(x: number, candles: CandlePoint[], liveCandle: CandlePoint | null, candleWidthSecs: number, padding: ChartPadding, canvasWidth: number, timestamp: number, windowSecs: number, gaps?: CandleGap[]): number; /** * Crosshair opacity: fades 1→0 over `fadeDistance` px as the crosshair * approaches the live dot at the right chart edge. A zero distance removes * the ramp while keeping the crosshair hidden at and beyond the live edge. */ export declare function computeCrosshairOpacity(scrubActive: boolean, scrubX: number, canvasWidth: number, paddingRight: number, fadeDistance?: number): number; /** * Full tooltip pill layout. Returns HIDDEN_TOOLTIP when the scrub is * inactive or no value can be interpolated at the current time. */ export declare function computeTooltipLayout(scrubActive: boolean, scrubX: number, scrubValue: number | null, scrubTime: number, padding: ChartPadding, canvasWidth: number, formatValue: (v: number) => string, formatTime: (t: number) => string, font: SkFont, /** Monospace advance width fallback when font measurement returns zero. */ monoCharWidth?: number, /** Where the pill sits relative to the scrub line. `"side"` offsets it right * (flipping left near the edge); `"top"`/`"bottom"` center it over the line, * clamped into the plot and pinned to the plot's top/bottom; `"point"` * centers it over the line and floats it just above the scrub dot (flipping * below when there's no room), deriving its Y from `scrubDotY`. */ placement?: "side" | "top" | "bottom" | "point", /** Render the value row. */ showValue?: boolean, /** Render the time row. */ showTime?: boolean, /** Canvas height in px — needed to pin `"bottom"`/`"point"` placement. */ canvasHeight?: number, /** Gap in px between the pill and the plot edge it's pinned to — and, for * `"point"`, between the scrub dot's center and the pill's nearest edge. */ margin?: number, /** Scrub intersection Y in canvas px (see {@link computeScrubDotY}) — the * anchor for `"point"` placement. -1 (default) falls back to a top pin. */ scrubDotY?: number): TooltipLayout; /** * Multi-series scrub tooltip — stacked lines (first row typically time, dim). */ export declare function computeTooltipLayoutMulti(scrubActive: boolean, scrubX: number, lines: { text: string; dim: boolean; }[], padding: ChartPadding, canvasWidth: number, font: SkFont, /** Monospace advance width fallback when font measurement returns zero. */ monoCharWidth?: number): TooltipLayout; /** * Candle-mode tooltip — 5 stacked rows: O, H, L, C (bright) + time (dim). */ export declare function computeCandleTooltipLayout(scrubActive: boolean, scrubX: number, candle: { open: number; high: number; low: number; close: number; } | null, scrubTime: number, padding: ChartPadding, canvasWidth: number, formatValue: (v: number) => string, formatTime: (t: number) => string, font: SkFont, monoCharWidth?: number, gap?: CandleGap | null, gapValue?: number | null): TooltipLayout; /** Two-row line-mode tooltip for an explicit semantic gap. */ export declare function computeLineGapTooltipLayout(scrubActive: boolean, scrubX: number, gap: CandleGap | null, scrubTime: number, padding: ChartPadding, canvasWidth: number, formatTime: (t: number) => string, font: SkFont, monoCharWidth?: number, placement?: "side" | "top" | "bottom" | "point", showLabel?: boolean, showTime?: boolean, canvasHeight?: number, margin?: number, scrubDotY?: number): TooltipLayout; /** Single-series scrub value at window time — extracted for tests. */ export declare function deriveScrubValueSingle(scrubActive: boolean, scrubTime: number, data: { time: number; value: number; }[]): number | null; /** * Right-gutter action-badge layout for scrub-action mode — two pills, vertically * centered on the locked reticle Y and anchored to the canvas right edge: * * - a **circular** icon button (the action), and * - a capsule **price pill** showing the formatted value, * * separated by {@link ACTION_BADGE_GAP}px (`[icon] [price]`). The price pill is * right-anchored in the gutter with its text horizontally centered; a lone icon * (icon-only) anchors to the plot's right edge, attached to the level line. * Either element is omitted when its content is empty (icon-only, or no price). * The union rect (`x/y/w/h`) is the single source of truth for the tap hit-test * (see {@link pointInRect}). When not locked / not laid out / both empty, returns * {@link HIDDEN_ACTION_BADGE}. * * The price text is sized to its actual visual bounds via `font.measureText` * (run only while a reticle is shown) so the readout stays exactly centered — * see the inline note on `priceTextX` below. */ export interface ActionBadgeLayout { /** Union hit rect (icon button + price pill). */ x: number; y: number; w: number; h: number; /** Whether the circular icon button is shown. */ hasIcon: boolean; /** Icon button circle center + radius (the glyph is centered on this by the overlay). */ iconCx: number; iconCy: number; iconR: number; /** Whether the price pill is shown. */ hasPrice: boolean; /** Price pill rect. */ priceX: number; priceY: number; priceW: number; priceH: number; /** Price text left x. */ priceTextX: number; /** Shared baseline y for the icon glyph + price text. */ textY: number; /** Formatted price string drawn in the price pill. */ priceText: string; /** Whether the badge is shown (locked + laid out + has content). */ visible: boolean; } export declare const HIDDEN_ACTION_BADGE: ActionBadgeLayout; export declare function computeActionBadgeLayout(locked: boolean, lockY: number, priceText: string, icon: string, canvasWidth: number, /** Plot's right edge (= canvasWidth - padding.right) — where the level line ends. */ plotRight: number, font: SkFont, marginEdge: number, padX: number, padY: number): ActionBadgeLayout; /** * X-axis time-badge layout for scrub-action mode — a small capsule pinned where * the reticle's vertical line meets the time axis (the bottom gutter), centered * on the reticle X and clamped to stay within the canvas. Display-only (no * hit-test): an opt-in readout of the date/time under the reticle, formatted by * the chart's `formatTime`. Hidden when not locked / not laid out / empty text. */ export interface TimeBadgeLayout { /** Pill rect. */ x: number; y: number; w: number; h: number; /** Time text left x (visual-bounds centered) + shared baseline y. */ textX: number; textY: number; /** Formatted time string drawn in the pill. */ timeText: string; /** Whether the badge is shown (locked + laid out + has text). */ visible: boolean; } export declare const HIDDEN_TIME_BADGE: TimeBadgeLayout; export declare function computeTimeBadgeLayout(locked: boolean, lockX: number, timeText: string, canvasWidth: number, /** Baseline y where the x-axis time labels sit (= canvasHeight - padding.bottom * + X_AXIS_LABEL_OFFSET_Y). The pill is vertically centered on this row so it * aligns with the axis values it overlays. */ labelBaselineY: number, font: SkFont, padX: number, padY: number, marginEdge: number): TimeBadgeLayout; /** Axis-aligned point-in-rect test with an optional touch-target inflation. */ export declare function pointInRect(px: number, py: number, rect: { x: number; y: number; w: number; h: number; }, slop?: number): boolean; /** Single-series crosshair tooltip — extracted for tests. */ export declare function deriveCrosshairTooltipSingle(scrubActive: boolean, scrubX: number, scrubTime: number, scrubValue: number | null, padding: ChartPadding, canvasWidth: number, formatValue: (v: number) => string, formatTime: (t: number) => string, font: SkFont, monoCharWidth?: number, placement?: "side" | "top" | "bottom" | "point", showValue?: boolean, showTime?: boolean, canvasHeight?: number, margin?: number, scrubDotY?: number): TooltipLayout; export {}; //# sourceMappingURL=crosshairShared.d.ts.map