/**
* BubbleChart — one labelled circle per row, on two measured axes, with a third
* quantity on each circle's area.
*
* ```tsx
*
*
*
*
*
*
*
*
* ```
*
* ## When this and not a scatter plot
*
* `ScatterChart` also maps a third quantity onto point area, through `sizeKey`,
* and it is the right component for a *series* of observations: many points of
* one colour, where the shape of the cloud is the finding and no single dot
* needs a name.
*
* This one is for a handful of named things. Each row is its own circle with
* its own colour and its own label written inside it, so the chart can be read
* entity by entity rather than as a distribution. Eight teams, twelve products,
* six regions — where the reader wants to find one of them and see where it
* sits.
*
* ## Area, not radius
*
* `sizeKey` maps to a circle's area. Doubling a radius quadruples the ink, so a
* chart that scaled the radius would show a doubled value as four times the
* size, and the reader would believe the picture. The scale runs over the whole
* data set, so one bubble's size means the same thing as another's.
*
* ## Labels are text, not SVG
*
* The names inside the bubbles are React Native `Text` in a layer over the
* plot, so they follow the theme's font and the platform's text scaling — SVG
* text does neither. A bubble too small to hold its own label is left without
* one rather than given an unreadable one; the readout still names it.
*/
import { type ReactNode } from 'react';
import { type ViewProps } from 'react-native';
import { type ChartAccessibilityProps } from '../../primitives/chart-accessibility.js';
type Layer = 'svg' | 'overlay' | 'header' | 'footer';
export type BubbleChartStatus = 'loading' | 'ready';
export type BubbleChartDatum = Record;
/** One bubble, resolved back to the row it came from. */
export interface BubbleChartPoint {
/** Index into `data`. */
index: number;
x: number;
y: number;
/** The value behind the area, when `sizeKey` is set. */
size: number | null;
/** The name written inside the circle, when `labelKey` is set. */
label: string;
/** The colour it was drawn in. */
color: string;
datum: BubbleChartDatum;
}
/** A bubble with its geometry resolved. Shared by every part. */
interface ResolvedBubble extends BubbleChartPoint {
/** Radius in points, off the area scale. */
r: number;
}
/**
* The bubble under the finger, for something rendered *inside* the chart. A
* readout in the card's header is outside this provider — use
* `onActivePointChange` for that.
*/
export declare function useBubbleChart(): {
activeIndex: number;
activePoint: ResolvedBubble | null;
};
export interface BubbleChartProps extends ViewProps, ChartAccessibilityProps {
className?: string;
/** The rows. One bubble each. */
data: BubbleChartDatum[];
/** Key holding the horizontal value. */
xDataKey?: string;
/** Key holding the vertical value. */
yDataKey?: string;
/**
* Key holding the third quantity, mapped to each bubble's *area*. Without it
* every bubble is drawn at the middle of `sizeRange` and the chart is a
* scatter plot with names on it.
*/
sizeKey?: string;
/** Key holding the name written inside the circle. */
labelKey?: string;
/**
* Key holding a colour for the row — either a CSS colour or a number from 1
* to 5 naming a `--color-chart-*` token. Without it the ramp cycles by row.
*/
colorKey?: string;
/**
* Smallest and largest radius `sizeKey` maps onto, in points. The largest is
* also what the plot holds back at every edge, so raising it costs room.
*/
sizeRange?: [number, number];
/**
* `loading` shows a still field of muted circles and dissolves it as the real
* bubbles grow in. One component throughout, rather than a spinner swapped
* for a chart — swapping loses the transition. Add a `BubbleChart.Skeleton`
* for something to stand in the plot meanwhile.
*/
status?: BubbleChartStatus;
/** Width ÷ height. `1` is the square shape a bubble field reads best in. */
aspectRatio?: number;
/** Milliseconds for the bubbles to grow in on mount. */
animationDuration?: number;
/** Milliseconds for the axes to settle after the data changes. */
domainDuration?: number;
/** Fix the horizontal axis instead of deriving it. */
xDomain?: [number, number];
/** Fix the vertical axis instead of deriving it. */
yDomain?: [number, number];
/** The bubble under the finger, and `null` when it lifts. */
onActivePointChange?: (point: BubbleChartPoint | null) => void;
children?: ReactNode;
}
/** Imperative handle: re-run the grow-in, for a "replay" control. */
export interface BubbleChartHandle {
replay: () => void;
}
export interface BubbleChartGridProps {
/**
* Horizontal rules across the plot.
*
* Eight, which is twice the four intervals an axis is divided into by
* default, so every second line carries a number and the ones between it are
* halves of a labelled step rather than an unrelated rhythm. Squares this
* size recede behind the circles; the coarse grid a smaller number draws
* reads as blocks laid over the plot.
*/
rows?: number;
/** Vertical rules up it. Both axes are measured, so both earn lines. */
columns?: number;
/** Dash pattern for the rules. Pass `undefined` for solid ones. */
dashArray?: string;
color?: string;
opacity?: number;
}
/** Reference lines both ways, so a bubble can be placed against two numbers. */
declare function BubbleChartGrid({ rows, columns, dashArray, color, opacity, }: BubbleChartGridProps): import("react").JSX.Element;
declare namespace BubbleChartGrid {
var displayName: string;
var layer: Layer;
}
export interface BubbleChartTrendProps {
/**
* The line's slope and intercept, and how tightly the cloud sits on it, once
* they have been computed. `r` runs 0 to 1: 1 is every bubble on the line,
* 0 is a cloud with no direction at all.
*
* Given here rather than left for the caller to work out, because the fit is
* already being computed to draw the line and doing it twice invites the two
* answers to disagree.
*
* It fires when the numbers change, not on every render that produced the
* same ones, so putting the fit straight into state is safe.
*/
onFit?: (fit: {
slope: number;
intercept: number;
r: number;
}) => void;
color?: string;
strokeWidth?: number;
/** Dash pattern. Dashed by default: the line is a reading, not a measurement. */
dashArray?: string;
opacity?: number;
}
/**
* The straight line that fits the cloud best, drawn across the plot.
*
* It is dashed and drawn under the circles, because it is not data — it is a
* summary of the data, and a solid rule through the middle of a field of
* bubbles reads as a value somebody plotted.
*
* The fit is least squares on the raw values, so it moves with the data rather
* than with the frame: resizing the chart never changes the line's meaning.
* Fewer than two bubbles, or every bubble on one vertical, has no line to draw
* and none is drawn.
*/
declare function BubbleChartTrend({ onFit, color, strokeWidth, dashArray, opacity, }: BubbleChartTrendProps): import("react").JSX.Element | null;
declare namespace BubbleChartTrend {
var displayName: string;
var layer: Layer;
}
export interface BubbleChartBubblesProps {
/**
* Fill opacity. Below 1 by default so that overlapping bubbles read as
* denser rather than hiding each other — in a crowded corner that overlap
* *is* the finding, and opaque circles erase it.
*/
opacity?: number;
/** One colour for every bubble, overriding the per-row ramp. */
color?: string;
}
/** The circles. */
declare function BubbleChartBubbles({ opacity, color }: BubbleChartBubblesProps): import("react").JSX.Element | null;
declare namespace BubbleChartBubbles {
var displayName: string;
var layer: Layer;
}
export interface BubbleChartSkeletonProps {
/** How many placeholder circles to scatter. */
count?: number;
color?: string;
}
/**
* The loading state: a still field of muted circles where the data will be.
*
* Deliberately still. A shimmer over a field of circles reads as them *moving*,
* which is the one thing this chart 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.
* It dissolves as the real bubbles grow in, and outlives the status change by
* exactly that long: cut at the frame the data lands, the placeholder would
* disappear before anything had replaced it.
*/
declare function BubbleChartSkeleton({ count, color }: BubbleChartSkeletonProps): import("react").JSX.Element | null;
declare namespace BubbleChartSkeleton {
var displayName: string;
var layer: Layer;
}
export interface BubbleChartLabelsProps {
/**
* Smallest radius a bubble may have and still be given its label. Below it
* the name is wider than the circle it names.
*/
minRadius?: number;
/** Turn a bubble into its label. Defaults to the value at `labelKey`. */
format?: (point: BubbleChartPoint) => string;
className?: string;
}
/**
* The names, written inside the circles.
*
* Real text over the plot rather than SVG text, so they follow the theme's font
* and the platform's text scaling. Each one rides the same domain tweens the
* circle under it does, so a label never lags the bubble it belongs to.
*
* A bubble too small to hold its name is left without one. Shrinking the text
* to fit would make it unreadable on exactly the bubbles the reader is
* squinting at already; the readout names those instead.
*/
declare function BubbleChartLabels({ minRadius, format, className, }: BubbleChartLabelsProps): import("react").JSX.Element | null;
declare namespace BubbleChartLabels {
var displayName: string;
var layer: Layer;
}
export interface BubbleChartQuadrantsProps {
/** Where the vertical rule stands. Defaults to the mean of the x values. */
x?: number;
/** Where the horizontal rule lies. Defaults to the mean of the y values. */
y?: number;
/** A word for each corner, written in the corner it belongs to. */
labels?: {
topLeft?: string;
topRight?: string;
bottomLeft?: string;
bottomRight?: string;
};
/** Tint the high-high and low-low corners. On by default. */
tint?: boolean;
color?: string;
className?: string;
}
/**
* A crosshair splitting the plot into four, with a name for each corner.
*
* A field of bubbles is usually read as four groups rather than as a cloud —
* which of these is doing well on both counts, which on neither — and without
* a divider the reader draws that line by eye, in a different place each time.
* Putting it on the chart makes it one line everybody sees.
*
* It stands at the mean of each axis by default, because that is the split the
* data itself argues for. Pass `x` and `y` for a target, a budget or last
* year's number — a threshold somebody decided rather than one the data
* produced.
*
* The tint marks the two corners a reading usually ends at. Turn it off where
* all four corners matter equally.
*/
declare function BubbleChartQuadrants({ x, y, labels, tint, color, className, }: BubbleChartQuadrantsProps): import("react").JSX.Element | null;
declare namespace BubbleChartQuadrants {
var displayName: string;
var layer: Layer;
}
export interface BubbleChartSizeKeyProps {
/** Which corner of the plot it sits in. */
placement?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
/** Turn a value into its label. Defaults to a compact number. */
format?: (value: number) => string;
/** A word for what the area means — "people", "revenue". */
label?: string;
className?: string;
}
/**
* Three nested circles saying what a bubble's area is worth.
*
* A bubble chart's third quantity is the one it cannot state: position can be
* read off the axes, but area has no axis, so a reader can see that one circle
* is bigger than another and has no way to know by how much. This is the only
* part of the chart that answers that.
*
* Nested and sharing a baseline, which is how a difference in area is compared
* — three circles in a row are three sizes, three circles inside one another
* are one scale.
*
* It needs a `sizeKey` on the chart. Without one every bubble is the same size
* and there is no scale to key.
*/
declare function BubbleChartSizeKey({ placement, format, label, className, }: BubbleChartSizeKeyProps): import("react").JSX.Element | null;
declare namespace BubbleChartSizeKey {
var displayName: string;
var layer: Layer;
}
export interface BubbleChartXAxisProps {
/**
* How many intervals to divide the axis into. Yields `ticks + 1` labels.
*
* Four, and the domain is rounded out to four steps to match, so the numbers
* come out round. Fewer leaves most of the grid unnamed — a line with nothing
* beside it is a line the reader has to count their way to.
*/
ticks?: number;
/** Turn a value into its label. Defaults to a compact number. */
format?: (value: number) => string;
/** What the axis measures, written under the numbers. */
label?: string;
className?: string;
}
/**
* The x labels, evenly along the axis.
*
* Evenly spaced, because this axis is a continuous scale rather than a list of
* rows. There is no bubble for a label to sit under.
*/
declare function BubbleChartXAxis({ ticks, format, label, className }: BubbleChartXAxisProps): import("react").JSX.Element;
declare namespace BubbleChartXAxis {
var displayName: string;
var layer: Layer;
var axis: "x";
}
export interface BubbleChartYAxisProps {
/**
* How many intervals to divide the axis into. Yields `ticks + 1` labels.
*
* Four, matching the four steps the domain is rounded out to and every second
* line of the default grid.
*/
ticks?: number;
/** Turn a value into its label. Defaults to a compact number. */
format?: (value: number) => string;
/** What the axis measures, written up the side of it. */
label?: string;
className?: string;
}
/**
* Value labels down the side, evenly over the axis, and the gutter they sit in.
*
* They land on every second line of the default grid rather than on all of
* them: a number beside every line of a grid fine enough to read against is a
* column of numbers, and the reader stops seeing the chart.
*/
declare function BubbleChartYAxis({ ticks, format, label, className }: BubbleChartYAxisProps): import("react").JSX.Element;
declare namespace BubbleChartYAxis {
var displayName: string;
var layer: Layer;
var axis: "y";
}
export interface BubbleChartTooltipProps {
/** Float a small readout beside the selected bubble. 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) => string;
/** Format the size value for the readout. Defaults to a compact number. */
formatSize?: (value: number) => string;
/** Floor on the touch target, for a chart whose smallest bubbles are tiny. */
hitRadius?: number;
className?: string;
}
/**
* The touch target, the selection it drives, and the readout that follows it.
*
* A touch picks the nearest bubble whose own circle — or the `hitRadius` floor,
* whichever is larger — reaches the finger. Nearest rather than topmost,
* because where bubbles overlap the one drawn last is not the one being aimed
* at.
*
* The search runs on the UI thread over flat arrays of already-projected
* coordinates, and only the *index* 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 bubble by distance is the nearest
* by distance-squared, and a square root per bubble per frame buys nothing.
*/
declare function BubbleChartTooltip({ showLabel, formatX, formatY, formatSize, hitRadius, className, }: BubbleChartTooltipProps): import("react").JSX.Element | null;
declare namespace BubbleChartTooltip {
var displayName: string;
var layer: Layer;
}
export interface BubbleChartLegendProps extends ViewProps {
className?: string;
/** Cap on how many bubbles are named. The rest are left to the readout. */
limit?: number;
}
/**
* A swatch and a name per bubble, for a chart whose circles are too small to
* carry their own labels.
*
* Drawn **under** the plot rather than floating in a corner of it. A key that
* overlays the drawing area competes with the bubbles for the space they are
* plotted in, and on a square chart there is no corner that is reliably empty —
* the position of a bubble is the data, so nowhere can be reserved for it.
*
* It lists rows rather than series, because in this chart a row *is* a
* category. Use it instead of `BubbleChart.Labels`, not beside it — the same
* names twice is the legend telling the reader what the plot already says.
*/
declare function BubbleChartLegend({ className, limit, ...props }: BubbleChartLegendProps): import("react").JSX.Element | null;
declare namespace BubbleChartLegend {
var displayName: string;
var layer: Layer;
}
export interface BubbleChartHeaderProps 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 — what the area means, usually. */
caption?: string;
/** Trailing slot — a control, a badge, a range picker. */
children?: ReactNode;
}
/**
* The strip above the plot: what the chart is of, what it currently reads, and
* what the size of a circle means.
*
* The caption earns its place here more than on most charts. Two axes and an
* area is three quantities, and a reader who is not told what the area is has
* no way to work it out from the picture.
*
* 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.
*/
declare function BubbleChartHeader({ className, title, value, caption, children, ...props }: BubbleChartHeaderProps): import("react").JSX.Element;
declare namespace BubbleChartHeader {
var displayName: string;
var layer: Layer;
}
export declare const BubbleChart: import("react").ForwardRefExoticComponent> & {
Header: typeof BubbleChartHeader;
Grid: typeof BubbleChartGrid;
Quadrants: typeof BubbleChartQuadrants;
Trend: typeof BubbleChartTrend;
Bubbles: typeof BubbleChartBubbles;
Labels: typeof BubbleChartLabels;
SizeKey: typeof BubbleChartSizeKey;
Skeleton: typeof BubbleChartSkeleton;
XAxis: typeof BubbleChartXAxis;
YAxis: typeof BubbleChartYAxis;
Tooltip: typeof BubbleChartTooltip;
Legend: typeof BubbleChartLegend;
};
export {};
//# sourceMappingURL=index.d.ts.map