/**
* 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