/** * ScatterChart — two quantities against each other, drawn on the UI thread. * * Every other chart in this library spaces its points evenly along the x-axis, * because their x is a position: twelve months are twelve equal steps whatever * the gaps between the dates behind them. A scatter plot is the one shape where * that is wrong. Both coordinates are *measured*, and the reader is being asked * to look for a relationship between them — spread the points evenly and the * relationship is the one thing you have thrown away. * * So this chart carries an x-domain as well as a y-domain, and both are tweened * when the data changes. * * ```tsx * * * * * * * * ``` * * As elsewhere, there are two layers and the parts sort themselves into the * right one: the geometry is SVG, and anything with text or a gesture on it is * a React Native view over the top. SVG text ignores the platform's text * scaling and the theme's font, and a gesture handler cannot be attached to an * SVG node at all. * * **Finding a point.** A crosshair that snaps to an x index — the way a line * chart's does — has nothing to snap to here, because there is no shared x and * two points can sit at the same one. Instead the nearest point to the finger * is resolved by distance, on the UI thread, and only within a radius: a touch * in an empty corner of the plot selects nothing rather than lighting up * whichever point happens to be least far away. The radius is generous, because * the points are a few pixels across and a fingertip is not. * * Colours come from the `--color-chart-*` tokens, so a chart follows the active * theme. Nothing here hardcodes a hex. */ import { type ReactNode } from 'react'; import { type ViewProps } from 'react-native'; import { type ChartAccessibilityProps } from '../../primitives/chart-accessibility.js'; type Layer = 'svg' | 'overlay' | 'header'; export type ScatterChartStatus = 'loading' | 'ready'; export type ScatterChartDatum = Record; /** One plotted point, resolved back to the row it came from. */ export interface ScatterChartPoint { /** Index into `data`. */ index: number; /** The series key this point belongs to. */ dataKey: string; x: number; y: number; datum: ScatterChartDatum; } /** * The selected point, for something rendered *inside* the chart. * * A readout usually belongs in the card's header, which is outside this * provider — use `onActivePointChange` for that. A hook cannot reach up out of * the subtree it is called in. */ export declare function useScatterChart(): { activePoint: ScatterChartPoint | null; xDataKey: string; }; export interface ScatterChartProps extends ViewProps, ChartAccessibilityProps { className?: string; /** The rows. Each one is a point, placed by two of its values. */ data: ScatterChartDatum[]; /** Key holding the x value. Unlike the other charts, this must be a number. */ xDataKey?: string; /** * `loading` draws a still field of muted dots and settles into the real ones * when it turns `ready`. One component throughout, rather than a spinner * swapped for a chart — swapping loses the transition. */ status?: ScatterChartStatus; /** Width ÷ height. `1` suits a scatter plot: neither axis is the important one. */ aspectRatio?: number; /** Milliseconds for the reveal on mount. Defaults to `650`. */ animationDuration?: number; /** Milliseconds for the axes to settle after the data changes. */ domainDuration?: number; /** Fix the x-axis instead of deriving it from the data. */ xDomain?: [number, number]; /** Fix the y-axis instead of deriving it from the data. */ yDomain?: [number, number]; /** * The point under the finger, and `null` when it lifts. This is how a readout * in the card's header gets its value — that header is outside the chart, so * it cannot use `useScatterChart`. * * Fires when the selection changes, not per frame. */ onActivePointChange?: (point: ScatterChartPoint | null) => void; /** Drop the axis padding so the field reaches the edges, for a thumbnail. */ compact?: boolean; children?: ReactNode; } /** Imperative handle: re-run the reveal on demand, for a "replay" control. */ export interface ScatterChartHandle { replay: () => void; } export interface ScatterChartGridProps { /** Horizontal rules across the plot. */ rows?: number; /** * Vertical rules down it. A scatter plot's x is a quantity, so it earns a * grid in both directions — a line chart's does not, because its x is a * label and a rule under a label divides nothing. */ columns?: number; color?: string; /** Dash pattern, e.g. `"4,6"`. Omit for a solid rule. */ dashArray?: string; opacity?: number; } /** Reference lines both ways. Drawn under everything, and not part of the reveal. */ declare function ScatterChartGrid({ rows, columns, color, dashArray, opacity, }: ScatterChartGridProps): import("react").JSX.Element; declare namespace ScatterChartGrid { var displayName: string; var layer: Layer; } export interface ScatterChartPointsProps { /** Key holding this series' y values. */ dataKey: string; /** * Fill colour. Defaults to the `--color-chart-*` token at `colorIndex`, so a * series follows the theme without the call site naming a colour. */ color?: string; /** Which `--color-chart-*` token to take when `color` is not given. */ colorIndex?: 1 | 2 | 3 | 4 | 5; /** Radius of a point, in points. Ignored when `sizeKey` is given. */ size?: number; /** * Key holding a third quantity, mapped to each point's *area* — a bubble * chart. Area rather than radius, because doubling a radius quadruples the * ink and the reader sees four times the value that is there. */ sizeKey?: string; /** Smallest and largest radius `sizeKey` maps onto. */ sizeRange?: [number, number]; /** * Fill opacity. Below 1 by default so that overlapping points read as denser * rather than hiding each other — in a crowded region that overlap *is* the * finding, and opaque dots erase it. */ opacity?: number; } /** One series, as a field of dots. */ declare function ScatterChartPoints({ dataKey, color, colorIndex, size, sizeKey, sizeRange, opacity, }: ScatterChartPointsProps): import("react").JSX.Element | null; declare namespace ScatterChartPoints { var displayName: string; var layer: Layer; } export interface ScatterChartSkeletonProps { /** How many placeholder dots to scatter. */ count?: number; color?: string; } /** * The loading state: a still field of muted dots where the data will be. * * Deliberately still. A shimmer over a field of dots reads as the points * *moving*, which is the one thing a scatter plot must never appear to do — * position is the entire message, and a loading state that implies it is * changing is a loading state that lies. * * The layout is deterministic rather than random, so it does not reshuffle on * every render of a component that may re-render several times while waiting. * * Still is not the same as abrupt, though. It dissolves as the real points grow * in, and outlives the status change by exactly that long — cut at the frame the * data lands, the placeholder disappears before anything has replaced it and the * plot is briefly empty. */ declare function ScatterChartSkeleton({ count, color }: ScatterChartSkeletonProps): import("react").JSX.Element | null; declare namespace ScatterChartSkeleton { var displayName: string; var layer: Layer; } export interface ScatterChartXAxisProps { /** How many intervals to divide the axis into. Yields `ticks + 1` labels. */ ticks?: number; /** Turn a value into its label. Defaults to a compact number. */ format?: (value: number) => string; className?: string; } /** * The x labels, evenly along the axis. * * Evenly spaced here — unlike a line chart's, where each label sits on the point * it names — because this axis is a continuous scale rather than a list of * rows. There is no point to sit on. */ declare function ScatterChartXAxis({ ticks, format, className }: ScatterChartXAxisProps): import("react").JSX.Element; declare namespace ScatterChartXAxis { var displayName: string; var layer: Layer; } export interface ScatterChartYAxisProps { /** How many intervals to divide the axis into. Yields `ticks + 1` labels. */ ticks?: number; /** Turn a value into its label. Defaults to a compact number. */ format?: (value: number) => string; className?: string; } /** Value labels down the side, one per grid line. */ declare function ScatterChartYAxis({ ticks, format, className }: ScatterChartYAxisProps): import("react").JSX.Element; declare namespace ScatterChartYAxis { var displayName: string; var layer: Layer; var axis: "y"; } export interface ScatterChartTooltipProps { /** Float a small readout beside the selected point. On by default. */ showLabel?: boolean; /** Format the x value for the readout. Defaults to a compact number. */ formatX?: (value: number) => string; /** Format the y value for the readout. Defaults to a compact number. */ formatY?: (value: number, key: string) => string; /** A heading for the readout, from the row — a name, a label, a category. */ formatTitle?: (datum: ScatterChartDatum) => string; /** How far from a point a touch still counts as being on it, in points. */ hitRadius?: number; } /** * The touch target, the selection it drives, and the readout that follows it. * * A line chart's crosshair snaps to an x index. That is not available here: * there is no shared x, and two points can sit on the same one. So the nearest * point is found by distance instead — and only within `hitRadius`, so a touch * in an empty corner selects nothing rather than lighting up whichever point is * least far away. * * The search runs on the UI thread over a flat array of already-projected * coordinates, and only the *identity* of the winner crosses back into JS, and * only when it changes. A drag across the plot therefore costs a handful of * re-renders rather than one per frame. * * Distances are compared squared. The nearest point by distance is the nearest * by distance-squared, and a square root per point per frame buys nothing. */ declare function ScatterChartTooltip({ showLabel, formatX, formatY, formatTitle, hitRadius, }: ScatterChartTooltipProps): import("react").JSX.Element | null; declare namespace ScatterChartTooltip { var displayName: string; var layer: Layer; } export interface ScatterChartLegendProps extends ViewProps { className?: string; /** Label per series key. A key with no label falls back to the key itself. */ labels?: Record; } /** * A swatch and a name per registered series. Sits in the top-left of the plot * by default — move it with `className`. */ declare function ScatterChartLegend({ className, labels, ...props }: ScatterChartLegendProps): import("react").JSX.Element | null; declare namespace ScatterChartLegend { var displayName: string; var layer: Layer; } export interface ScatterChartHeaderProps extends ViewProps { className?: string; /** Small line above the value — what the chart is of. */ title?: string; /** The readout. The largest thing on the card, and the first thing read. */ value?: string; /** One muted line under the value — a period, a comparison, a total. */ caption?: string; /** Prettier names for the series keys, as the legend takes. */ labels?: Record; /** * Draw a swatch and a name per series along the trailing edge. Prefer this to * `ScatterChart.Legend` on a chart that has a header: the legend floats over * the plot, where it competes with the points for the same corner. */ legend?: boolean; /** Trailing slot — a control, a badge, a range picker. Wins over `legend`. */ children?: ReactNode; } /** * The strip above the plot: what the chart is of, what it currently reads, and * what the colours mean. * * The value is not derived here. A readout that follows the finger belongs to * whoever owns the data — take it from `onActivePointChange` and pass the * formatted string down, so one header can show a summary when nothing is * pressed and a point's values when something is. */ declare function ScatterChartHeader({ className, title, value, caption, labels, legend, children, ...props }: ScatterChartHeaderProps): import("react").JSX.Element; declare namespace ScatterChartHeader { var displayName: string; var layer: Layer; } export declare const ScatterChart: import("react").ForwardRefExoticComponent> & { Header: typeof ScatterChartHeader; Grid: typeof ScatterChartGrid; Points: typeof ScatterChartPoints; Skeleton: typeof ScatterChartSkeleton; XAxis: typeof ScatterChartXAxis; YAxis: typeof ScatterChartYAxis; Tooltip: typeof ScatterChartTooltip; Legend: typeof ScatterChartLegend; }; export {}; //# sourceMappingURL=index.d.ts.map