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